From 478163c6621b88c5cdf5e60a92f29aa729e60b00 Mon Sep 17 00:00:00 2001 From: gigaherz Date: Wed, 20 Jan 2010 18:49:41 +0000 Subject: [PATCH] I implemented somethign I called pcsx2hostfs. It's a IOP driver which uses a virtual device implemented into pcsx2's emulation of the iop memory map, and not existing in the real ps2, which could be used to allow "host:" access from ps2 applications. THe pcsx2-side implementation currently is only hackedup to allow reading a hardcoded path, and it would probably not work with normal homebrew yet. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2459 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/IopMem.cpp | 19 +- pcsx2/pcsx2hostfs.cpp | 199 ++++ pcsx2/windows/VCprojects/pcsx2_2008.vcproj | 4 + pcsx2hostfs/CHANGELOG | 64 ++ pcsx2hostfs/LICENSE | 174 ++++ pcsx2hostfs/Makefile | 134 +++ pcsx2hostfs/README | 46 + pcsx2hostfs/doxy.conf | 1098 ++++++++++++++++++++ pcsx2hostfs/ee/Makefile | 73 ++ pcsx2hostfs/ee/Rules.make | 45 + pcsx2hostfs/ee/crt0.s | 113 ++ pcsx2hostfs/ee/excepHandler.c | 170 +++ pcsx2hostfs/ee/excepHandler.h | 14 + pcsx2hostfs/ee/exceptions.S | 225 ++++ pcsx2hostfs/ee/linkfile.lcf | 53 + pcsx2hostfs/ee/pcsx2hostfs_ldr.elf | Bin 0 -> 282735 bytes pcsx2hostfs/ee/ps2link.c | 809 ++++++++++++++ pcsx2hostfs/ee/ps2regs.h | 286 +++++ pcsx2hostfs/ee/r5900_regs.h | 73 ++ pcsx2hostfs/include/byteorder.h | 42 + pcsx2hostfs/include/hostlink.h | 22 + pcsx2hostfs/iop/Makefile | 43 + pcsx2hostfs/iop/Rules.make | 54 + pcsx2hostfs/iop/excepHandler.c | 119 +++ pcsx2hostfs/iop/excepHandler.h | 13 + pcsx2hostfs/iop/imports.lst | 63 ++ pcsx2hostfs/iop/irx_imports.h | 28 + pcsx2hostfs/iop/mipsirx.x | 80 ++ pcsx2hostfs/iop/net_fio.c | 230 ++++ pcsx2hostfs/iop/net_fio.h | 33 + pcsx2hostfs/iop/net_fsys.c | 306 ++++++ pcsx2hostfs/iop/nprintf.c | 82 ++ pcsx2hostfs/iop/pcsx2hostfs.irx | Bin 0 -> 20305 bytes pcsx2hostfs/iop/ps2link.c | 55 + pcsx2hostfs/iop/tty.c | 105 ++ pcsx2hostfs/pcsx2hostfs.sln | 20 + pcsx2hostfs/pcsx2hostfs.vcproj | 265 +++++ 37 files changed, 5155 insertions(+), 4 deletions(-) create mode 100644 pcsx2/pcsx2hostfs.cpp create mode 100644 pcsx2hostfs/CHANGELOG create mode 100644 pcsx2hostfs/LICENSE create mode 100644 pcsx2hostfs/Makefile create mode 100644 pcsx2hostfs/README create mode 100644 pcsx2hostfs/doxy.conf create mode 100644 pcsx2hostfs/ee/Makefile create mode 100644 pcsx2hostfs/ee/Rules.make create mode 100644 pcsx2hostfs/ee/crt0.s create mode 100644 pcsx2hostfs/ee/excepHandler.c create mode 100644 pcsx2hostfs/ee/excepHandler.h create mode 100644 pcsx2hostfs/ee/exceptions.S create mode 100644 pcsx2hostfs/ee/linkfile.lcf create mode 100644 pcsx2hostfs/ee/pcsx2hostfs_ldr.elf create mode 100644 pcsx2hostfs/ee/ps2link.c create mode 100644 pcsx2hostfs/ee/ps2regs.h create mode 100644 pcsx2hostfs/ee/r5900_regs.h create mode 100644 pcsx2hostfs/include/byteorder.h create mode 100644 pcsx2hostfs/include/hostlink.h create mode 100644 pcsx2hostfs/iop/Makefile create mode 100644 pcsx2hostfs/iop/Rules.make create mode 100644 pcsx2hostfs/iop/excepHandler.c create mode 100644 pcsx2hostfs/iop/excepHandler.h create mode 100644 pcsx2hostfs/iop/imports.lst create mode 100644 pcsx2hostfs/iop/irx_imports.h create mode 100644 pcsx2hostfs/iop/mipsirx.x create mode 100644 pcsx2hostfs/iop/net_fio.c create mode 100644 pcsx2hostfs/iop/net_fio.h create mode 100644 pcsx2hostfs/iop/net_fsys.c create mode 100644 pcsx2hostfs/iop/nprintf.c create mode 100644 pcsx2hostfs/iop/pcsx2hostfs.irx create mode 100644 pcsx2hostfs/iop/ps2link.c create mode 100644 pcsx2hostfs/iop/tty.c create mode 100644 pcsx2hostfs/pcsx2hostfs.sln create mode 100644 pcsx2hostfs/pcsx2hostfs.vcproj diff --git a/pcsx2/IopMem.cpp b/pcsx2/IopMem.cpp index a30dd4682d..4d32c87180 100644 --- a/pcsx2/IopMem.cpp +++ b/pcsx2/IopMem.cpp @@ -32,6 +32,10 @@ static const uint m_psxMemSize = 0x00010000 + // psxP 0x00000100 ; // psxS +// TODO: move to a header +void Pcsx2HostFSwrite32(u32 addr, u32 value); +u32 Pcsx2HostFSread32(u32 addr); + void psxMemAlloc() { if( m_psxAllMem == NULL ) @@ -238,7 +242,7 @@ u32 __fastcall iopMemRead32(u32 mem) if (t == 0x1d00) { u32 ret; - switch(mem & 0xF0) + switch(mem & 0x8F0) { case 0x00: ret= psHu32(SBUS_F200); @@ -258,6 +262,9 @@ u32 __fastcall iopMemRead32(u32 mem) case 0x60: ret = 0; break; + case 0x800: + return Pcsx2HostFSread32(mem); + default: ret = psxHu32(mem); break; @@ -353,7 +360,7 @@ void __fastcall iopMemWrite16(u32 mem, u16 value) { if (t == 0x1d00) { - switch (mem & 0xf0) + switch (mem & 0x8f0) { case 0x10: // write to ps2 mem @@ -379,7 +386,6 @@ void __fastcall iopMemWrite16(u32 mem, u16 value) case 0x60: psHu32(SBUS_F260) = 0; return; - } psxSu16(mem) = value; return; } @@ -425,7 +431,7 @@ void __fastcall iopMemWrite32(u32 mem, u32 value) if (t == 0x1d00) { MEM_LOG("iop Sif reg write %x value %x", mem, value); - switch (mem & 0xf0) + switch (mem & 0x8f0) { case 0x00: // EE write path (EE/IOP readable) return; // this is the IOP, so read-only (do nothing) @@ -462,6 +468,11 @@ void __fastcall iopMemWrite32(u32 mem, u32 value) case 0x60: psHu32(SBUS_F260) = 0; return; + + case 0x800: + Pcsx2HostFSwrite32(mem, value); + return; + } psxSu32(mem) = value; diff --git a/pcsx2/pcsx2hostfs.cpp b/pcsx2/pcsx2hostfs.cpp new file mode 100644 index 0000000000..bf6e24bc7b --- /dev/null +++ b/pcsx2/pcsx2hostfs.cpp @@ -0,0 +1,199 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2009 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 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 PCSX2. + * If not, see . + */ + + +#include "PrecompiledHeader.h" +#include "IopCommon.h" +#include "Utilities/Console.h" +#include + +#pragma optimize("", off) + +#define PS2E_FIO_OPEN_CMD 0xcc2e0101 +#define PS2E_FIO_CLOSE_CMD 0xcc2e0102 +#define PS2E_FIO_READ_CMD 0xcc2e0103 +#define PS2E_FIO_WRITE_CMD 0xcc2e0104 +#define PS2E_FIO_LSEEK_CMD 0xcc2e0105 +#define PS2E_FIO_OPENDIR_CMD 0xcc2e0106 +#define PS2E_FIO_CLOSEDIR_CMD 0xcc2e0107 +#define PS2E_FIO_READDIR_CMD 0xcc2e0108 +#define PS2E_FIO_REMOVE_CMD 0xcc2e0109 +#define PS2E_FIO_MKDIR_CMD 0xcc2e010a +#define PS2E_FIO_RMDIR_CMD 0xcc2e010b + +#define PS2E_FIO_PRINTF_CMD 0xcc2e0201 + +#define PS2E_FIO_MAGIC 'E2SP' + +u32 functionId; +u32 paramsAddress; +u32 paramsLength; +u32 returnValue; + +#define IOP_RDONLY 0x0001 +#define IOP_WRONLY 0x0002 +#define IOP_RDWR 0x0003 +#define IOP_NBLOCK 0x0010 +#define IOP_APPEND 0x0100 +#define IOP_CREAT 0x0200 +#define IOP_TRUNC 0x0400 +#define IOP_NOWAIT 0x8000 + +int pcsx2fio_open_file(char *path, int flags) +{ + path++; + int mode = 0; + + switch(flags&IOP_RDWR) + { + case IOP_RDONLY: + mode = O_RDONLY; + break; + case IOP_WRONLY: + mode = O_WRONLY; + break; + case IOP_RDWR: + mode = O_RDWR; + break; + } + if(flags&IOP_CREAT) + mode |= O_CREAT; + if(flags&IOP_APPEND) + mode |= O_APPEND; + if(flags&IOP_TRUNC) + mode |= O_TRUNC; + + return open(path,mode); +} + +int pcsx2fio_close_file(int fd) +{ + return close(fd); +} + +int pcsx2fio_lseek_file(int fd, unsigned int offset, int whence) +{ + return lseek(fd,offset,whence); +} + +int pcsx2fio_write_file(int fd, char *buf, int length) +{ + return write(fd,buf,length); +} + +int pcsx2fio_read_file(int fd, char *buf, int length) +{ + return read(fd,buf,length); +} + +int pcsx2fio_remove(char *name) +{ + return unlink(name); +} + +int pcsx2fio_mkdir(char *name, int mode) +{ + return mkdir(name); +} + +int pcsx2fio_rmdir(char *name) +{ + return rmdir(name); +} + +int pcsx2fio_open_dir(char *path) +{ + return -1; +} + +int pcsx2fio_read_dir(int fd, void *buf) +{ + return -1; +} + +int pcsx2fio_close_dir(int fd) +{ + return -1; +} + +int pcsx2fio_write_tty(const char* text, int length) +{ + wxString s = wxString::FromUTF8(text,length); + + return printf("%s",s.ToAscii()); +} + +#define PARAM(offset,type) (*(type*)(buffer+(offset))) +#define PARAMP(offset,type) (type*)iopPhysMem(PARAM(offset,u32)) +#define PARAMSTR(offset) ((char*)(buffer+(offset))) +void Pcsx2HostFsExec() +{ + u8* buffer = (u8*)iopPhysMem(paramsAddress); + + switch(functionId) + { + case PS2E_FIO_OPEN_CMD: returnValue = pcsx2fio_open_file(PARAMSTR(4),PARAM(0,s32)); break; + case PS2E_FIO_CLOSE_CMD: returnValue = pcsx2fio_close_file(PARAM(0,s32)); break; + case PS2E_FIO_READ_CMD: returnValue = pcsx2fio_read_file(PARAM(0,s32),PARAMP(4,char),PARAM(8,s32)); break; + case PS2E_FIO_WRITE_CMD: returnValue = pcsx2fio_write_file(PARAM(0,s32),PARAMP(4,char),PARAM(8,s32)); break; + case PS2E_FIO_LSEEK_CMD: returnValue = pcsx2fio_lseek_file(PARAM(0,s32),PARAM(4,s32),PARAM(8,s32)); break; + case PS2E_FIO_REMOVE_CMD: returnValue = pcsx2fio_remove(PARAMSTR(0)); break; + case PS2E_FIO_OPENDIR_CMD: returnValue = pcsx2fio_open_dir(PARAMSTR(0)); break; + case PS2E_FIO_CLOSEDIR_CMD: returnValue = pcsx2fio_close_dir(PARAM(0,s32)); break; + case PS2E_FIO_READDIR_CMD: returnValue = pcsx2fio_read_dir(PARAM(0,s32),PARAMP(4,void)); break; + case PS2E_FIO_MKDIR_CMD: returnValue = pcsx2fio_mkdir(PARAMSTR(4),PARAM(0,s32)); break; + case PS2E_FIO_RMDIR_CMD: returnValue = pcsx2fio_rmdir(PARAMSTR(0)); break; + + case PS2E_FIO_PRINTF_CMD: returnValue = pcsx2fio_write_tty(PARAMSTR(0),paramsLength); break; + } +} + +void Pcsx2HostFSwrite32(u32 addr, u32 value) +{ + switch(addr&0xF) + { + case 0: + if(value==1) + Pcsx2HostFsExec(); + break; + case 4: + paramsLength = value; + break; + case 8: + paramsAddress = value; + break; + case 12: + functionId = value; + break; + } +} + +u32 Pcsx2HostFSread32(u32 addr) +{ + switch(addr&0xF) + { + case 0: + return PS2E_FIO_MAGIC; + break; + case 4: + break; + case 8: + break; + case 12: + return returnValue; + break; + } + return 0; +} \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index ccd8b7f123..fe3c53837e 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -1114,6 +1114,10 @@ RelativePath="..\..\IopSio2.h" > + + diff --git a/pcsx2hostfs/CHANGELOG b/pcsx2hostfs/CHANGELOG new file mode 100644 index 0000000000..6a4be6d3db --- /dev/null +++ b/pcsx2hostfs/CHANGELOG @@ -0,0 +1,64 @@ +PS2Link (C) 2003 Tord Lindstrom (pukko@home.se) + (C) 2003,2004 adresd (adresd_ps2dev@yahoo.com) + (C) 2004 Lukasz Bruun (mail@lukasz.dk) + (C) 2006 Drakonite (makeshift_ps2dev@123mail.org) +------------------------------------------------------------------------ + +2006-02-18 Version 1.46 + - Added Cached Config support + - Extra config disabled, at least while cached config is enabled + - Some code cleanup and reordering + - More consistant environment (no longer has sio2man and mcman + loaded when booting from MC, assuming cached_cfg enabled) + - Boots from essentially anywhere now + - Some code uglyfications + - Elf in bin/ is now packed using ps2-packer + - Lots of version bumping from beta test versions + - Other stuff + +2006-01-25 Version 1.32 + - Cleaned up and repaired Makefiles + - Disabled 'check' dependency of make dist as it seems to + be broken, dist was broken anyways so it doesn't seem like + check is being used + - Builtin_irx made default out of svn + - Bumped version for tag + +2004-09-25 Version 1.30 + - Added IOP exception handling + - Changed IPCONFIG.DAT & EXTRA.CNF a little and added better + parsing code. + +2004-05-24 Version 1.24 + - Made changes for compiling with ps2sdk, introduction of imports.lst/.h + and many changes in IOP part to use ioman.h. + - Set default values for irx_mod pointers and sizes for cached modules + to ensure they were in .bss and not trashed on pko_reset. + - Set default values for ip parameters for the same reason as above. + - New command writemem + - Can load irx files from a defined file in IPCONFIG.DAT + - All-In-One elf (irx's embedded in the elf) build target. + +2004-02-08 Version 1.23 + - Some new commands stop/start vu, dump mem, dump reg, gsexec + +2004-02-06 Version 1.22 + - HOST: getdir support (so ps2 can get filelist from host:, using ioman calls) + +2004-01-29 Version 1.21 + - Fixed Host loading (both IRX and ipconfig) - prob with reset (CLEARSPU), but runs fine. + - Added sbv for prefix checking, to remove LMB for mc load. + - Added screen/console exception dump selection. + - Consistent IOP reset for both HOST and other FS load methods. + - Removed DMS specific loading, module loading made more general. + - Handles being run from ANY mc dir now. + - Cleaned up highloading version. + +2003-12-31 Version 1.2 + - Binary Release Version + +2003-12-15 Version 1.1 + - Made compatible with ps2drv, for eth and HDD access together. + +2003-05-12 Version 1.0 + - First release diff --git a/pcsx2hostfs/LICENSE b/pcsx2hostfs/LICENSE new file mode 100644 index 0000000000..d754d0d730 --- /dev/null +++ b/pcsx2hostfs/LICENSE @@ -0,0 +1,174 @@ + +COPYRIGHT FOR PS2LINK +---------------------------------------------------------------------------- + + Copyright (c) 2003 Tord Lindstrom (pukko@home.se) + (c) 2003 adresd (adresd_ps2dev@yahoo.com) + (c) 2004 Khaled Daham + (c) 2004 Nicolas 'Pixel' Noble + + The Academic Free License + v. 2.0 + +This Academic Free License (the "License") applies to any original work +of authorship (the "Original Work") whose owner (the "Licensor") has +placed the following notice immediately following the copyright notice +for the Original Work: + + *Licensed under the Academic Free License version 2.0* + +1) *Grant of Copyright License.* Licensor hereby grants You a +world-wide, royalty-free, non-exclusive, perpetual, sublicenseable +license to do the following: + + a) to reproduce the Original Work in copies; + + b) to prepare derivative works ("Derivative Works") based upon the + Original Work; + + c) to distribute copies of the Original Work and Derivative Works to + the public; + + d) to perform the Original Work publicly; and + + e) to display the Original Work publicly. + +2) *Grant of Patent License.* Licensor hereby grants You a world-wide, +royalty-free, non-exclusive, perpetual, sublicenseable license, under +patent claims owned or controlled by the Licensor that are embodied in +the Original Work as furnished by the Licensor, to make, use, sell and +offer for sale the Original Work and Derivative Works. + +3) *Grant of Source Code License.* The term "Source Code" means the +preferred form of the Original Work for making modifications to it and +all available documentation describing how to modify the Original Work. +Licensor hereby agrees to provide a machine-readable copy of the Source +Code of the Original Work along with each copy of the Original Work that +Licensor distributes. Licensor reserves the right to satisfy this +obligation by placing a machine-readable copy of the Source Code in an +information repository reasonably calculated to permit inexpensive and +convenient access by You for as long as Licensor continues to distribute +the Original Work, and by publishing the address of that information +repository in a notice immediately following the copyright notice that +applies to the Original Work. + +4) *Exclusions From License Grant. *Neither the names of Licensor, nor +the names of any contributors to the Original Work, nor any of their +trademarks or service marks, may be used to endorse or promote products +derived from this Original Work without express prior written permission +of the Licensor. Nothing in this License shall be deemed to grant any +rights to trademarks, copyrights, patents, trade secrets or any other +intellectual property of Licensor except as expressly stated herein. No +patent license is granted to make, use, sell or offer to sell +embodiments of any patent claims other than the licensed claims defined +in Section 2. No right is granted to the trademarks of Licensor even if +such marks are included in the Original Work. Nothing in this License +shall be interpreted to prohibit Licensor from licensing under different +terms from this License any Original Work that Licensor otherwise would +have a right to license. + +5) This section intentionally omitted. + +6) *Attribution Rights.* You must retain, in the Source Code of any +Derivative Works that You create, all copyright, patent or trademark +notices from the Source Code of the Original Work, as well as any +notices of licensing and any descriptive text identified therein as an +"Attribution Notice." You must cause the Source Code for any Derivative +Works that You create to carry a prominent Attribution Notice reasonably +calculated to inform recipients that You have modified the Original Work. + +7) *Warranty of Provenance and Disclaimer of Warranty.* Licensor +warrants that the copyright in and to the Original Work and the patent +rights granted herein by Licensor are owned by the Licensor or are +sublicensed to You under the terms of this License with the permission +of the contributor(s) of those copyrights and patent rights. Except as +expressly stated in the immediately proceeding sentence, the Original +Work is provided under this License on an "AS IS" BASIS and WITHOUT +WARRANTY, either express or implied, including, without limitation, the +warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL +WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential +part of this License. No license to Original Work is granted hereunder +except under this disclaimer. + +8) *Limitation of Liability.* Under no circumstances and under no legal +theory, whether in tort (including negligence), contract, or otherwise, +shall the Licensor be liable to any person for any direct, indirect, +special, incidental, or consequential damages of any character arising +as a result of this License or the use of the Original Work including, +without limitation, damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages +or losses. This limitation of liability shall not apply to liability for +death or personal injury resulting from Licensor's negligence to the +extent applicable law prohibits such limitation. Some jurisdictions do +not allow the exclusion or limitation of incidental or consequential +damages, so this exclusion and limitation may not apply to You. + +9) *Acceptance and Termination.* If You distribute copies of the +Original Work or a Derivative Work, You must make a reasonable effort +under the circumstances to obtain the express assent of recipients to +the terms of this License. Nothing else but this License (or another +written agreement between Licensor and You) grants You permission to +create Derivative Works based upon the Original Work or to exercise any +of the rights granted in Section 1 herein, and any attempt to do so +except under the terms of this License (or another written agreement +between Licensor and You) is expressly prohibited by U.S. copyright law, +the equivalent laws of other countries, and by international treaty. +Therefore, by exercising any of the rights granted to You in Section 1 +herein, You indicate Your acceptance of this License and all of its +terms and conditions. + +10) *Termination for Patent Action.* This License shall terminate +automatically and You may no longer exercise any of the rights granted +to You by this License as of the date You commence an action, including +a cross-claim or counterclaim, for patent infringement (i) against +Licensor with respect to a patent applicable to software or (ii) against +any entity with respect to a patent applicable to the Original Work (but +excluding combinations of the Original Work with other software or +hardware). + +11) *Jurisdiction, Venue and Governing Law.* Any action or suit relating +to this License may be brought only in the courts of a jurisdiction +wherein the Licensor resides or in which Licensor conducts its primary +business, and under the laws of that jurisdiction excluding its +conflict-of-law provisions. The application of the United Nations +Convention on Contracts for the International Sale of Goods is expressly +excluded. Any use of the Original Work outside the scope of this License +or after its termination shall be subject to the requirements and +penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., the +equivalent laws of other countries, and international treaty. This +section shall survive the termination of this License. + +12) *Attorneys Fees.* In any action to enforce the terms of this License +or seeking damages relating thereto, the prevailing party shall be +entitled to recover its costs and expenses, including, without +limitation, reasonable attorneys' fees and costs incurred in connection +with such action, including any appeal of such action. This section +shall survive the termination of this License. + +13) *Miscellaneous.* This License represents the complete agreement +concerning the subject matter hereof. If any provision of this License +is held to be unenforceable, such provision shall be reformed only to +the extent necessary to make it enforceable. + +14) *Definition of "You" in This License.* "You" throughout this +License, whether in upper or lower case, means an individual or a legal +entity exercising rights under, and complying with all of the terms of, +this License. For legal entities, "You" includes any entity that +controls, is controlled by, or is under common control with you. For +purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or +more of the outstanding shares, or (iii) beneficial ownership of such +entity. + +15) *Right to Use.* You may use the Original Work in all ways not +otherwise restricted or conditioned by this License or by law, and +Licensor promises not to interfere with or be responsible for such uses +by You. + +This license is Copyright (C) 2003 Lawrence E. Rosen. All rights +reserved. Permission is hereby granted to copy and distribute this +license without modification. This license may not be modified without +the express written permission of its copyright owner. + diff --git a/pcsx2hostfs/Makefile b/pcsx2hostfs/Makefile new file mode 100644 index 0000000000..9c3df22c58 --- /dev/null +++ b/pcsx2hostfs/Makefile @@ -0,0 +1,134 @@ +# Compilation variables + +# Set this to 1 to enable debug mode +DEBUG = 1 + +# Set this to 1 to build a highloading version, 0 for normal low version +LOADHIGH = 0 + +# Set this to 1 to build ps2link with all the needed IRX builtins +BUILTIN_IRXS = 1 + +# Set this to 1 to enable caching of config files +CACHED_CFG = 1 + +# Set this to 1 to enable zero-copy on fileio writes. +ZEROCOPY = 0 + +# Set this to 1 to power off the ps2 when the reset button is tapped +# otherwise it will try and reset ps2link +PWOFFONRESET = 1 + +# Set this to 1 to hook the kernel CreateThread/DeleteThread calls. +# Note that this will cause problems when loading PS2LINK.ELF from PS2LINK... +HOOK_THREADS = 0 + +# Set this to 1 to enable screenshots. +# Note that this adds a dependency with libgraph and libdma +SCREENSHOTS = 0 + +# Set to the path where ps2eth is located +PS2ETH = $(PS2DEV)/ps2eth + +SHELL=/usr/bin/env bash +BIN2O=$(PS2SDK)/bin/bin2o +RM=rm -f + +# +# You shouldn't need to modify anything below this point +# + +include $(PS2SDK)/Defs.make + +EEFILES=ee/pcsx2hostfs_ldr.elf + +IRXFILES=iop/pcsx2hostfs.irx $(PS2SDK)/iop/irx/ioptrap.irx + +VARIABLES=DEBUG=$(DEBUG) LOADHIGH=$(LOADHIGH) BUILTIN_IRXS=$(BUILTIN_IRXS) ZEROCOPY=$(ZEROCOPY) PWOFFONRESET=$(PWOFFONRESET) CACHED_CFG=$(CACHED_CFG) HOOK_THREADS=$(HOOK_THREADS) SCREENSHOTS=$(SCREENSHOTS) + +ifeq ($(BUILTIN_IRXS),1) +TARGETS = iop builtins ee +else +TARGETS = ee iop +endif + +all: $(TARGETS) +ifneq ($(BUILTIN_IRXS),1) + @for file in $(IRXFILES); do \ + new=`echo $${file/*\//}|tr "[:lower:]" "[:upper:]"`; \ + cp $$file bin/$$new; \ + done; +endif + @for file in $(EEFILES); do \ + new=`echo $${file/*\//}|tr "[:lower:]" "[:upper:]"`; \ + cp $$file bin/$$new; \ + done; + +ee: + $(VARIABLES) $(MAKE) -C ee + +iop: + $(VARIABLES) $(MAKE) -C iop + +clean: + $(MAKE) -C ee clean + $(MAKE) -C iop clean + +check: + $(VARIABLES) $(MAKE) -C ee check + +# Creates a zip from what you have +dist: all + @rm -rf dist + @mkdir -p dist/ps2link +ifneq ($(BUILTIN_IRXS),1) + @for file in $(IRXFILES); do \ + new=`echo $${file/*\//}|tr "[:lower:]" "[:upper:]"`; \ + cp $$file dist/ps2link/$$new; \ + done; +endif + @for file in $(EEFILES); do \ + new=`echo $${file/*\//}|tr "[:lower:]" "[:upper:]"`; \ + cp $$file dist/ps2link/$$new; \ + done; + cd dist; \ + tar -jcf ps2link.tar.bz2 ps2link/ + +RELEASE_FILES=bin/*IRX bin/*DAT bin/*cnf bin/*ELF LICENSE README +# +# Creates zip with iso and all necessary files of last release +release: + @rm -rf RELEASE + @mkdir -p RELEASE + @VERSION=`cvs log Makefile | grep -A 1 symbolic | tail -1 | awk '{print substr($$1, 0, length($$1)-1)}'`; \ + cd RELEASE; \ + cvs co -r $$VERSION ps2link; \ + cd ps2link; \ + make; \ + make check; \ + mkdir -p bin; \ + for file in $(IRXFILES); do \ + new=`echo $${file/*\//}|tr "[:lower:]" "[:upper:]"`; \ + cp $$file bin/$$new; \ + done; \ + for file in $(EEFILES); do \ + new=`echo $${file/*\//}|tr "[:lower:]" "[:upper:]"`; \ + cp $$file bin/$$new; \ + done; \ + dd if=/dev/zero of=bin/dummy bs=1024 count=28672; \ + ps2mkisofs -o ps2link_$$VERSION.iso bin/; \ + rm bin/dummy; \ + tar -jcf ps2link_$$VERSION.tbz $(RELEASE_FILES) ps2link_$$VERSION.iso + +docs: + doxygen doxy.conf + +builtins: + @for file in $(IRXFILES); do \ + basefile=$${file/*\//}; \ + basefile=$${basefile/\.*/}; \ + echo "Embedding IRX file $$basefile"; \ + $(BIN2O) $$file ee/$${basefile}_irx.o _binary_$${basefile}_irx; \ + done; + +.PHONY: iop ee diff --git a/pcsx2hostfs/README b/pcsx2hostfs/README new file mode 100644 index 0000000000..ead275d150 --- /dev/null +++ b/pcsx2hostfs/README @@ -0,0 +1,46 @@ +PS2Link (C) 2003 Tord Lindstrom (pukko@home.se) + (C) 2003,2004 adresd (adresd_ps2dev@yahoo.com) + (C) 2003,2004,2005 Khaled (khaled@w-arts.com) +------------------------------------------------------------------------ + +Please read the file LICENSE regarding PS2Link licensing. + +PS2Link is a 'bootloader' which, used together with an Ethernet driver and +a TCP/IP stack, enables you to download and execute software on your PS2. + +It is designed to run from memory card, cdrom or host drives. + +It loads all IRX's at startup and IPCONFIG.DAT for the network settings. +The IRX's and the IPCONFIG.DAT should be in the directory which PS2LINK is loaded from. + +PS2Link requires the following IRX modules: +PS2LINK.IRX from: ps2link +PS2DEV9.IRX ps2sdk +PS2IP.IRX ps2sdk +IOPTRAP.IRX ps2sdk +POWEROFF.IRX ps2sdk +PS2SMAP.IRX ps2eth + +Building ps2link requires two projects PS2SDK and PS2ETH. + +For building against ps2sdk make sure PS2SDK is set to your ps2sdk release +dir. If you do not have ps2sdk built, check it out from cvs and set PS2SDK and +PS2SDKSRC = checked out ps2sdk dir, do 'make' and 'make release'. + +Credit for the icon logo goes to Revolt from #ps2dev. + +NOTES + WARNINGS: +ALL IRX FILENAMES SHOULD BE UPPERCASE. +IPCONFIG.DAT FILENAME SHOULD BE UPPERCASE. + +IPCONFIG.DAT uses the following format: +PS2IPADDRESS NETMASK GATEWAYIP +seperated by a single space. + +You can load addition IRX's by specifying EXTRACNF file in IPCONFIG.DAT. It +will load the EXTRACNF file and and load all irx's terminated with ';' in the order +they are listed in the EXTRACNF file. By default the EXTRACNF setting is +disabled (commented) with '#' in IPCONFIG.DAT, remove the '#' the load the +EXTRACNF file. + +If you have any questions or bugreports about ps2link go to forums.ps2dev.org. diff --git a/pcsx2hostfs/doxy.conf b/pcsx2hostfs/doxy.conf new file mode 100644 index 0000000000..f3f3f3c698 --- /dev/null +++ b/pcsx2hostfs/doxy.conf @@ -0,0 +1,1098 @@ +# Doxyfile 1.3.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = ps2link + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1.2 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ps2link-doc + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is used +# as the annotated text. Otherwise, the brief description is used as-is. If left +# blank, the following values are used ("$name" is automatically replaced with the +# name of the entity): "The $name class" "The $name widget" "The $name file" +# "is" "provides" "specifies" "contains" "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = warn.log + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = */samples/* + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superseded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes that +# lay further from the root node will be omitted. Note that setting this option to +# 1 or 2 may greatly reduce the computation time needed for large code bases. Also +# note that a graph may be further truncated if the graph's image dimensions are +# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). +# If 0 is used for the depth value (the default), the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/pcsx2hostfs/ee/Makefile b/pcsx2hostfs/ee/Makefile new file mode 100644 index 0000000000..be5e3bd8c6 --- /dev/null +++ b/pcsx2hostfs/ee/Makefile @@ -0,0 +1,73 @@ +EE_BIN = pcsx2hostfs_ldr.elf +EE_OBJS = ps2link.o excepHandler.o exceptions.o +EE_INCS += -I../include + +# This is for the sbv patch +SBVLITE = $(PS2SDK)/sbv +EE_INCS += -I$(SBVLITE)/include +EE_LDFLAGS += -L$(SBVLITE)/lib +EE_LIBS += -lpatches -ldebug +ifeq ($(SCREENSHOTS),1) +EE_LIBS += -lpacket -ldma +endif + +# Normal low loading version +ifeq ($(BUILTIN_IRXS),1) +LOADADDR = 0xa8000 +else +LOADADDR = 0xc0000 +endif +STACKADDR = 0x100000 + +# This is to build a highloading version +ifeq ($(LOADHIGH),1) +ifeq ($(BUILTIN_IRXS),1) +LOADADDR = 0x1ee8000 +else +LOADADDR = 0x1f00000 +endif +STACKADDR = 0x1f40000 +EE_CFLAGS += -D_LOADHIGHVER +endif + +# This is to builtin the IRXs into ps2link +ifeq ($(BUILTIN_IRXS),1) +EE_CFLAGS += -DBUILTIN_IRXS +EE_LDFLAGS += pcsx2hostfs_irx.o ioptrap_irx.o +endif + +ifeq ($(BUILTIN_IRXS),1) +ifeq ($(CACHED_CFG),1) +EE_CFLAGS += -DUSE_CACHED_CFG +endif +endif + +# This is to enable the debug mode into ps2link +ifeq ($(DEBUG),1) +EE_CFLAGS += -DDEBUG -g +endif + +# Enable screenshot functionality +ifeq ($(SCREENSHOTS),1) +EE_CFLAGS += -DSCREENSHOTS +endif + +LDPARAMS := -Wl,--defsym -Wl,_stack_size=0x8000 -Wl,--defsym -Wl,_stack=$(STACKADDR) +EE_LDFLAGS += -Wl,-Ttext -Wl,$(LOADADDR) $(LDPARAMS) + +ifeq ($(DEBUG),1) +EE_LDFLAGS += -g +else +EE_LDFLAGS += -s +endif + +all: $(EE_BIN) + +clean: + -rm -f $(EE_OBJS) $(EE_BIN) + +check: $(EE_BIN) + @ee-readelf -l $(EE_BIN) | awk ' /LOAD/ { if ((spare = (-($$4 + $$6) + ("'$(STACKADDR)'" - "0x8000"))) <= 0) { printf("PS2Link is too big, %i (0x%x) bytes missing\n", -spare, -spare); exit -1; } else { printf("PS2Link has %i (0x%x) spare bytes\n", spare, spare) } } ' + +include $(PS2SDK)/Defs.make +include Rules.make diff --git a/pcsx2hostfs/ee/Rules.make b/pcsx2hostfs/ee/Rules.make new file mode 100644 index 0000000000..f3ff43b83f --- /dev/null +++ b/pcsx2hostfs/ee/Rules.make @@ -0,0 +1,45 @@ + +# Include directories +EE_INCS := -I$(PS2SDK)/ee/include -I$(PS2SDK)/common/include -I. $(EE_INCS) + +# C compiler flags +EE_CFLAGS := -D_EE -O2 -G0 -Wall $(EE_CFLAGS) + +# C++ compiler flags +EE_CXXFLAGS := -D_EE -O2 -G0 -Wall $(EE_CXXFLAGS) + +# Linker flags +EE_LDFLAGS := -L$(PS2SDK)/ee/lib $(EE_LDFLAGS) + +# Assembler flags +EE_ASFLAGS := -G0 $(EE_ASFLAGS) + +# Link with following libraries. This is a special case, and instead of +# allowing the user to override the library order, we always make sure +# libkernel is the last library to be linked. +EE_LIBS += -lc -lkernel + +# Externally defined variables: EE_BIN, EE_OBJS, EE_LIB + +# These macros can be used to simplify certain build rules. +EE_C_COMPILE = $(EE_CC) $(EE_CFLAGS) $(EE_INCS) +EE_CXX_COMPILE = $(EE_CC) $(EE_CXXFLAGS) $(EE_INCS) + +%.o : %.c + $(EE_CC) $(EE_CFLAGS) $(EE_INCS) -c $< -o $@ + +%.o : %.cpp + $(EE_CXX) $(EE_CXXFLAGS) $(EE_INCS) -c $< -o $@ + +%.o : %.S + $(EE_CC) $(EE_CFLAGS) $(EE_INCS) -c $< -o $@ + +%.o : %.s + $(EE_AS) $(EE_ASFLAGS) $< -o $@ + +$(EE_BIN) : $(EE_OBJS) $(PS2SDK)/ee/startup/crt0.o + $(EE_CC) -nostartfiles -T$(PS2SDK)/ee/startup/linkfile $(EE_LDFLAGS) \ + -o $(EE_BIN) $(PS2SDK)/ee/startup/crt0.o $(EE_OBJS) $(EE_LIBS) + +$(EE_LIB) : $(EE_OBJS) + $(EE_AR) cru $(EE_LIB) $(EE_OBJS) diff --git a/pcsx2hostfs/ee/crt0.s b/pcsx2hostfs/ee/crt0.s new file mode 100644 index 0000000000..1e3ef51530 --- /dev/null +++ b/pcsx2hostfs/ee/crt0.s @@ -0,0 +1,113 @@ +.set noat +.set noreorder + +.global _start +.global _exit + +.text + nop + nop +_start: + lui $2,%hi(_args_ptr) + addiu $2,$2, %lo(_args_ptr) + sw $4,($2) +# Clear bss +zerobss: + lui $2,%hi(_fbss) + lui $3,%hi(_end) + addiu $2,$2,%lo(_fbss) + addiu $3,$3,%lo(_end) +loop: + nop + nop + sq $0,($2) + sltu $1,$2,$3 + bne $1,$0,loop + addiu $2,$2,16 +# Thread + lui $4,%hi(_gp) + lui $5,%hi(_stack) + lui $6,%hi(_stack_size) + lui $7,%hi(_args) + lui $8,%hi(_exit) + addiu $4,$4,%lo(_gp) + addiu $5,$5,%lo(_stack) + addiu $6,$6,%lo(_stack_size) + addiu $7,$7,%lo(_args) + addiu $8,$8,%lo(_exit) + move $28,$4 + addiu $3,$0,60 + syscall + move $29, $2 + +# Heap + addiu $3,$0,61 + lui $4,%hi(_end) + addiu $4,$4,%lo(_end) + lui $5,%hi(_heap_size) + addiu $5,$5,%lo(_heap_size) + syscall + nop + +# Cache + li $3, 100 + move $4,$0 + syscall + nop + +# Jump main + ei + + # Check if we got args from system + lui $2, %hi(_args) + addiu $2, %lo(_args) + lw $3, 0($2) + bnez $3, _gotArgv # Started w (Load)ExecPS2 + nop + + # Check args via $a0 + lui $2,%hi(_args_ptr) + addiu $2,$2,%lo(_args_ptr) + lw $3,0($2) + + beqzl $3, _goMain + addu $4, $0, $0 + + addiu $2, $3, 4 + b _gotArgv + nop + +_gotArgv: + lw $4, ($2) + addiu $5, $2, 4 +_goMain: + jal main + nop + + # Check if we got our args in $a0 again + la $4, _args_ptr + lw $5, 0($4) + beqz $5, _exit + nop +_root: + lw $6, 0($5) + sw $0, 0($6) + # Call ExitDeleteThread() + addiu $3, $0, 36 + syscall + nop +_exit: +# Exit + # Should call Exit(retval) if not called by pukklink + addiu $3,$0,35 + syscall + move $4, $2 + nop + + .bss + .align 6 +_args: .space 256+16*4+4 + + .data +_args_ptr: + .space 4 diff --git a/pcsx2hostfs/ee/excepHandler.c b/pcsx2hostfs/ee/excepHandler.c new file mode 100644 index 0000000000..307ae3ce8c --- /dev/null +++ b/pcsx2hostfs/ee/excepHandler.c @@ -0,0 +1,170 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include "stdio.h" +#include +#include +#include "excepHandler.h" +#include "debug.h" + +extern int _gp; +int userThreadID; +int excepscrdump; + +//////////////////////////////////////////////////////////////////////// +typedef union +{ + unsigned int uint128 __attribute__(( mode(TI) )); + unsigned long uint64[2]; +} eeReg __attribute((packed)); + +//////////////////////////////////////////////////////////////////////// +// Prototypes +void pkoDebug(int cause, int badvaddr, int status, int epc, eeReg *regs); + +extern void pkoExceptionHandler(void); + +//////////////////////////////////////////////////////////////////////// +static const unsigned char regName[32][5] = +{ + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "t8", "t9", "s0", "s1", "s2", "s3", "s4", "s5", + "s6", "s7", "k0", "k1", "gp", "sp", "fp", "ra" +}; + +static char codeTxt[14][24] = +{ + "Interrupt", "TLB modification", "TLB load/inst fetch", "TLB store", + "Address load/inst fetch", "Address store", "Bus error (instr)", + "Bus error (data)", "Syscall", "Breakpoint", "Reserved instruction", + "Coprocessor unusable", "Arithmetic overflow", "Trap" +}; + +char _exceptionStack[8*1024] __attribute__((aligned(16))); +eeReg _savedRegs[32+4] __attribute__((aligned(16))); + +//////////////////////////////////////////////////////////////////////// +// The 'ee exception handler', only dumps registers to console or screen atm +void +pkoDebug(int cause, int badvaddr, int status, int epc, eeReg *regs) +{ + int i; + int code; + // extern void printf(char *, ...); + static void (* excpPrintf)(const char *, ...); + + FlushCache(0); + FlushCache(2); + +#if 1 + if (userThreadID) { + TerminateThread(userThreadID); + DeleteThread(userThreadID); + } +#endif + + code = cause & 0x7c; + + if (excepscrdump) + { + init_scr(); + excpPrintf = scr_printf; + } + else excpPrintf = (void*)printf; + + excpPrintf("\n\n EE Exception handler: %s exception\n\n", + codeTxt[code>>2]); + + excpPrintf(" Cause %08X BadVAddr %08X Status %08X EPC %08X\n\n", + cause, badvaddr, status, epc); + + for(i = 0; i < 32/2; i++) { + excpPrintf("%4s: %016lX%016lX %4s: %016lX%016lX\n", + regName[i], regs[i].uint64[1], regs[i].uint64[0], + regName[i+16], regs[i+16].uint64[1], regs[i+16].uint64[0]); + } + excpPrintf("\n"); + SleepThread(); +} + +//////////////////////////////////////////////////////////////////////// +// The 'iop exception handler', only dumps registers to console or screen atm + +void iopException(int cause, int badvaddr, int status, int epc, u32 *regs, int repc, char* name) +{ + int i; + int code; + // extern void printf(char *, ...); + static void (* excpPrintf)(const char *, ...); + + FlushCache(0); + FlushCache(2); + +#if 1 + if (userThreadID) { + TerminateThread(userThreadID); + DeleteThread(userThreadID); + } +#endif + + code = cause & 0x7c; + + if(excepscrdump) + { + init_scr(); + excpPrintf = scr_printf; + } + else excpPrintf = (void*)printf; + + excpPrintf("\n\n IOP Exception handler: %s exception\n\n", + codeTxt[code>>2]); + + excpPrintf(" Module Name \"%s\" Relative EPC %08X\n\n", + name, repc); + + + excpPrintf(" Cause %08X BadVAddr %08X Status %08X EPC %08X\n\n", + cause, badvaddr, status, epc); + + for(i = 0; i < 32/4; i++) + { + excpPrintf(" %4s: %08X %4s: %08X %4s: %08X %4s: %08X\n", + regName[i], regs[i], regName[i+8], regs[i+8], + regName[i+16], regs[i+16], regName[i+24], regs[i+24]); + } + + excpPrintf("\n"); + + + + SleepThread(); +} + + +//////////////////////////////////////////////////////////////////////// +// Installs ee exception handlers for the 'usual' exceptions and iop +// exception callback +void +installExceptionHandlers(void) +{ + int i; + + // Skip exception #8 (syscall) & 9 (breakpoint) + for (i = 1; i < 4; i++) { + SetVTLBRefillHandler(i, pkoExceptionHandler); + } + for (i = 4; i < 8; i++) { + SetVCommonHandler(i, pkoExceptionHandler); + } + for (i = 10; i < 14; i++) { + SetVCommonHandler(i, pkoExceptionHandler); + } + +} + diff --git a/pcsx2hostfs/ee/excepHandler.h b/pcsx2hostfs/ee/excepHandler.h new file mode 100644 index 0000000000..23bcb6b2f4 --- /dev/null +++ b/pcsx2hostfs/ee/excepHandler.h @@ -0,0 +1,14 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +void installExceptionHandlers(void); +void iopException(int cause, int badvaddr, int status, int epc, u32 *regs, int repc, char* name); + +#endif diff --git a/pcsx2hostfs/ee/exceptions.S b/pcsx2hostfs/ee/exceptions.S new file mode 100644 index 0000000000..80b8e21eda --- /dev/null +++ b/pcsx2hostfs/ee/exceptions.S @@ -0,0 +1,225 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +# ASM exception handlers + +#include "r5900_regs.h" + +.set noat +.set noreorder + + +.text +.p2align 4 + + .global _savedRegs + + .global pkoStepBP + .ent pkoStepBP +pkoStepBP: + # Should check for cause in cop0 Cause reg + # If Bp, increase EPC (DO NOT PUT 'break' in a branch delay slot!!!) + mfc0 k0, EPC # cop0 EPC + addiu k0, k0, 4 # Step over breakpoint + mtc0 k0, EPC + sync.p + eret + .end pkoStepBP + + + # Save all user regs + # Save HI/LO, SR, BadVAddr, Cause, EPC, ErrorEPC, + # ShiftAmount, cop0: $24, $25 + # Save float regs?? + # Set EPC to debugger + # Set stack to 'exception stack' + # eret + .global pkoExceptionHandler + .ent pkoExceptionHandler +pkoExceptionHandler: + la k0, _savedRegs + sq $0, 0x00(k0) + sq at, 0x10(k0) + sq v0, 0x20(k0) + sq v1, 0x30(k0) + sq a0, 0x40(k0) + sq a1, 0x50(k0) + sq a2, 0x60(k0) + sq a3, 0x70(k0) + sq t0, 0x80(k0) + sq t1, 0x90(k0) + sq t2, 0xa0(k0) + sq t3, 0xb0(k0) + sq t4, 0xc0(k0) + sq t5, 0xd0(k0) + sq t6, 0xe0(k0) + sq t7, 0xf0(k0) + sq t8, 0x100(k0) + sq t9, 0x110(k0) + sq s0, 0x120(k0) + sq s1, 0x130(k0) + sq s2, 0x140(k0) + sq s3, 0x150(k0) + sq s4, 0x160(k0) + sq s5, 0x170(k0) + sq s6, 0x180(k0) + sq s7, 0x190(k0) +# sq k0, 0x1a0(k0) # $k0 + sq zero, 0x1a0(k0) # zero instead + sq k1, 0x1b0(k0) # $k1 + sq gp, 0x1c0(k0) + sq sp, 0x1d0(k0) # sp + sq fp, 0x1e0(k0) + sq ra, 0x1f0(k0) # $ra + + pmfhi t0 # HI + pmflo t1 # LO + sq t0, 0x200(k0) + sq t1, 0x210(k0) + + mfc0 t0, BadVAddr # Cop0 state regs + mfc0 t1, Status + sw t0, 0x220(k0) + sw t1, 0x224(k0) + + mfc0 t0, Cause + mfc0 t1, EPC + sw t0, 0x228(k0) + sw t1, 0x22c(k0) + + # Kernel saves these two also.. + mfc0 t0, DEPC + mfc0 t1, PerfCnt + sw t0, 0x230(k0) + sw t1, 0x234(k0) + + mfsa t0 + sw t0, 0x238(k0) + + # Use our own stack.. + la sp, _exceptionStack+0x2000-16 + la gp, _gp # Use exception handlers _gp + + # Return from exception and start 'debugger' + mfc0 a0, Cause # arg0 + mfc0 a1, BadVAddr + mfc0 a2, Status + mfc0 a3, EPC + addu t0, zero, k0 # arg4 = registers + move t1, sp + la k0, pkoDebug + mtc0 k0, EPC # eret return address + sync.p + mfc0 k0, Status # check this out.. + li v0, 0xfffffffe + and k0, v0 + mtc0 k0, Status + sync.p + nop + nop + nop + nop + eret + nop + .end pkoExceptionHandler + + + + # Put EE in kernel mode + # Restore all user regs etc + # Restore PC? & Stack ptr + # Restore interrupt sources + # Jump to EPC + .ent pkoReturnFromDebug + .global pkoReturnFromDebug +pkoReturnFromDebug: + + lui t1, 0x1 +_disable: + di + sync + mfc0 t0, Status + and t0, t1 + beqz t0, _disable + nop + + la k0, _savedRegs + + lq t0, 0x200(k0) + lq t1, 0x210(k0) + pmthi t0 # HI + pmtlo t1 # LO + + lw t0, 0x220(k0) + lw t1, 0x224(k0) + mtc0 t0, BadVAddr + mtc0 t1, Status + + lw t0, 0x228(k0) + lw t1, 0x22c(k0) + mtc0 t0, Cause + mtc0 t1, EPC + + # Kernel saves these two also.. + lw t0, 0x230(k0) + lw t1, 0x234(k0) + mtc0 t0, DEPC + mtc0 t1, PerfCnt + + # Shift Amount reg + lw t0, 0x238(k0) + mtsa t0 + + +# ori t2, 0xff +# sw t2, 0(k1) + + # lq $0, 0x00(k0) + lq $1, 0x10(k0) + lq $2, 0x20(k0) + lq $3, 0x30(k0) + lq $4, 0x40(k0) + lq $5, 0x50(k0) + lq $6, 0x60(k0) + lq $7, 0x70(k0) + lq $8, 0x80(k0) + lq $9, 0x90(k0) + lq $10, 0xa0(k0) + lq $11, 0xb0(k0) + lq $12, 0xc0(k0) + lq $13, 0xd0(k0) + lq $14, 0xe0(k0) + lq $15, 0xf0(k0) + lq $16, 0x100(k0) + lq $17, 0x110(k0) + lq $18, 0x120(k0) + lq $19, 0x130(k0) + lq $10, 0x140(k0) + lq $21, 0x150(k0) + lq $22, 0x160(k0) + lq $23, 0x170(k0) + lq $24, 0x180(k0) + lq $25, 0x190(k0) +# lq $26, 0x1a0(k0) # $k0 + lq $27, 0x1b0(k0) # $k1 + lq $28, 0x1c0(k0) + lq $29, 0x1d0(k0) # $sp + lq $30, 0x1e0(k0) +# lq $31, 0x1f0(k0) # $ra + + lw ra, 0x22c(k0) + # Guess one should have some check here, and only advance PC if + # we are going to step over a Breakpoint or something + # (i.e. do stuff depending on Cause) + addiu ra, 4 + sync.p + ei + sync.p + + jr ra + nop + .end pkoReturnFromDebug diff --git a/pcsx2hostfs/ee/linkfile.lcf b/pcsx2hostfs/ee/linkfile.lcf new file mode 100644 index 0000000000..9a1857b303 --- /dev/null +++ b/pcsx2hostfs/ee/linkfile.lcf @@ -0,0 +1,53 @@ +_heap_size = 1*1024; +_stack_size = 0x2000; + +ENTRY(_start); +SECTIONS { + + .text 0xc0000 : { + *(.text) + *(.rodata) + } + .reginfo ALIGN(128) : { + *(.reginfo) + } + .data ALIGN(128) : { + *(.data) + } + .rdata ALIGN(128) : { + *(.rdata) + } + _gp = ALIGN(128) + 0x7ff0; + .lit4 ALIGN(128) : { + *(.lit4) + } + .lit8 ALIGN(128) : { + *(.lit8) + } + .sdata ALIGN(128) : { + *(.sdata) + } + + .sbss ALIGN(128) (NOLOAD) : { /* uninitialized data */ + _fbss = . ; + *(.scommon) + *(.sbss) + } + .bss ALIGN(128) (NOLOAD) : { /* uninitialized data */ + *(.bss) + } + .COMMON ALIGN(128) (NOLOAD) : { /* uninitialized data */ + *(COMMON) + } + _end_bss = .; + _end = . ; + PROVIDE (end = .); + __lc_bh = . ; + . += _heap_size ; + __lc_eh = .; + .stack ALIGN(128) (NOLOAD) : { /* stack space */ + _stack = .; + . += _stack_size; + _stack_end = .; + } +} diff --git a/pcsx2hostfs/ee/pcsx2hostfs_ldr.elf b/pcsx2hostfs/ee/pcsx2hostfs_ldr.elf new file mode 100644 index 0000000000000000000000000000000000000000..1a7a10f4ef6bcda8d5615f17f138e94d684a11a9 GIT binary patch literal 282735 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�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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include "cd.h" +#include "hostlink.h" +#include "excepHandler.h" +#include "stdio.h" + +#include + +#define WELCOME_STRING "Welcome to pcsx2hostfs v1.52\n" + +#ifdef DEBUG +#define dbgprintf(args...) printf(args) +#define dbgscr_printf(args...) scr_printf(args) +#else +#define dbgprintf(args...) do { } while(0) +#define dbgscr_printf(args...) do { } while(0) +#endif + +//#define BGCOLOR ((volatile unsigned long*)0x120000e0) +//#define GS_SET_BGCOLOR(R,G,B) *BGCOLOR = ((u64)(R)<< 0) | ((u64)(G)<< 8) | ((u64)(B)<< 16) + +#define IRX_BUFFER_BASE 0x1F80000 +int irx_buffer_addr = 0; + +//////////////////////////////////////////////////////////////////////// +// Globals +extern int userThreadID; + +// Argv name+path & just path +char elfName[256] __attribute__((aligned(16))); +char elfPath[256]; + +#ifndef BUILTIN_IRXS +char ioptrap_path[PKO_MAX_PATH]; +char pcsx2hostfs_path[PKO_MAX_PATH]; + +void *ioptrap_mod = NULL, *pcsx2hostfs_mod = NULL; +int ioptrap_size = 0, pcsx2hostfs_size = 0; +#else +extern unsigned char _binary_ioptrap_irx_start[], _binary_pcsx2hostfs_irx_start[]; +extern unsigned char _binary_ioptrap_irx_end[], _binary_pcsx2hostfs_irx_end[]; +static unsigned int _binary_ioptrap_irx_size, _binary_pcsx2hostfs_irx_size; +#endif + +char *imgcmd = "rom0:UDNL rom0:EELOADCNF"; + +// Flags for which type of boot +#define B_CD 1 +#define B_MC 2 +#define B_HOST 3 +#define B_CC 4 +#define B_UNKN 5 +int boot; + +//////////////////////////////////////////////////////////////////////// +// Prototypes +static void loadModules(void); +static void pkoLoadModule(char *path, int argc, char *argv); +static void getIpConfig(void); + +//////////////////////////////////////////////////////////////////////// +#define IPCONF_MAX_LEN 1024 + +char if_conf[IPCONF_MAX_LEN] = ""; +char fExtraConfig[256]; +int load_extra_conf = 0; +int if_conf_len = 0; + +char ip[16] __attribute__((aligned(16))) = "192.168.0.10"; +char netmask[16] __attribute__((aligned(16))) = "255.255.255.0"; +char gw[16] __attribute__((aligned(16))) = "192.168.0.1"; + +//////////////////////////////////////////////////////////////////////// +// Parse network configuration from IPCONFIG.DAT +// Note: parsing really should be made more robust... + +static int getBufferValue(char* result, char* buffer, u32 buffer_size, char* field) +{ + u32 len = strlen(field); + u32 i = 0; + s32 j = 0; + char* start = 0; + char* end = 0; + + while(strncmp(&buffer[i], field, len) != 0) + { + i++; + if(i == buffer_size) return -1; + } + + // Look for # comment + j = i; + + while((j > 0) && (buffer[j] != '\n') && (buffer[j] !='\r')) + { + j--; + + if(buffer[j] == '#') + return -2; + } + + + while(buffer[i] != '=') + { + i++; + if(i == buffer_size) return -3; + } + i++; + + while(buffer[i] == ' ') + { + i++; + if(i == buffer_size) return -4; + } + i++; + + if((buffer[i] != '\r') && (buffer[i] != '\n') && (buffer[i] != ';')) + { + start = &buffer[i]; + } + else + { + return -5; + } + + while(buffer[i] != ';') + { + i++; + if(i == buffer_size) return -5; + } + + end = &buffer[i]; + + len = end - start; + + if(len > 256) return -6; + + strncpy(result, start, len); + + return 0; +} + +static void +getIpConfig(void) +{ + int fd; + int i; + int t; + int len; + char c; + char buf[IPCONF_MAX_LEN+256]; + static char path[256]; + + if (boot == B_CD) { + sprintf(path, "%s%s;1", elfPath, "IPCONFIG.DAT"); + } + else if (boot == B_CC) { + strcpy(path, "mc0:/BOOT/IPCONFIG.DAT"); + } + else { + sprintf(path, "%s%s", elfPath, "IPCONFIG.DAT"); + } + fd = fioOpen(path, O_RDONLY); + + if (fd < 0) + { + scr_printf("Could not find IPCONFIG.DAT, using defaults\n" + "Net config: %s %s %s\n", ip, netmask, gw); + // Set defaults + memset(if_conf, 0x00, IPCONF_MAX_LEN); + i = 0; + strncpy(&if_conf[i], ip, 15); + i += strlen(ip) + 1; + + strncpy(&if_conf[i], netmask, 15); + i += strlen(netmask) + 1; + + strncpy(&if_conf[i], gw, 15); + i += strlen(gw) + 1; + + if_conf_len = i; + dbgscr_printf("conf len %d\n", if_conf_len); + return; + } + + memset(if_conf, 0x00, IPCONF_MAX_LEN); + memset(buf, 0x00, IPCONF_MAX_LEN+256); + + len = fioRead(fd, buf, IPCONF_MAX_LEN + 256 - 1); // Let the last byte be '\0' + fioClose(fd); + + if (len < 0) { + dbgprintf("Error reading ipconfig.dat\n"); + return; + } + + dbgscr_printf("ipconfig: Read %d bytes\n", len); + + i = 0; + // Clear out spaces (and potential ending CR/LF) + while ((c = buf[i]) != '\0') { + if ((c == ' ') || (c == '\r') || (c == '\n')) + buf[i] = '\0'; + i++; + } + + scr_printf("Net config: "); + for (t = 0, i = 0; t < 3; t++) { + strncpy(&if_conf[i], &buf[i], 15); + scr_printf("%s ", &if_conf[i]); + i += strlen(&if_conf[i]) + 1; + } + scr_printf("\n"); + // get extra config filename + +#ifndef USE_CACHED_CFG + if(getBufferValue(fExtraConfig, buf, len, "EXTRACNF") == 0) + { + scr_printf("extra conf: %s\n", fExtraConfig); + + load_extra_conf = 1; + } + else + { + load_extra_conf = 0; + } +#else + load_extra_conf = 0; +#endif + + if_conf_len = i; +} + +void +getExtraConfig() +{ + int fd, size, ret; + char *buf, *ptr, *ptr2; + fd = fioOpen(fExtraConfig, O_RDONLY); + + if ( fd < 0 ) + { + scr_printf("failed to open extra conf file\n"); + return; + } + + size = fioLseek(fd, 0, SEEK_END); + fioLseek(fd, 0, SEEK_SET); + buf = malloc(size + 1); + ret = fioRead(fd, buf, size); + buf[size] = 0; + fioClose(fd); + ptr = buf; + ptr2 = buf; + while(ptr < buf+size) { + ptr2 = strstr(ptr, ";"); + if ( ptr2 == 0 ) { + break; + } + ptr[ptr2-ptr] = 0; + scr_printf("loading %s\n", ptr); + pkoLoadModule(ptr, 0, NULL); + ptr = ptr2+1; + } + free(buf); + return; +} + +//////////////////////////////////////////////////////////////////////// +// Wrapper to load irx module from disc/rom +static void +pkoLoadModule(char *path, int argc, char *argv) +{ + int ret; + + ret = SifLoadModule(path, argc, argv); + if (ret < 0) { + scr_printf("Could not load module %s: %d\n", path, ret); + SleepThread(); + } + dbgscr_printf("[%d] returned\n", ret); +} + +#ifndef BUILTIN_IRXS +/* Load a module into RAM. */ +void * modbuf_load(const char *filename, int *filesize) +{ + void *res = NULL; + int fd = -1, size; + + if ((fd = fioOpen(filename, O_RDONLY)) < 0) + goto out; + + if ((size = fioLseek(fd, 0, SEEK_END)) < 0) + goto out; + + fioLseek(fd, 0, SEEK_SET); + fioLseek(fd, 0, SEEK_SET); + + res = (void *)irx_buffer_addr; + irx_buffer_addr += size + 32 - (size % 16); + dbgscr_printf(" modbuf_load : %s , %d (0x%X)\n",filename,size,(int)res); + + if (fioRead(fd, res, size) != size) + res = NULL; + + if (filesize) + *filesize = size; + +out: + if (fd >= 0) + fioClose(fd); + + return res; +} + +static int loadHostModBuffers() +{ + if (irx_buffer_addr == 0) + { + irx_buffer_addr = IRX_BUFFER_BASE; + + getIpConfig(); + + if (!(ioptrap_mod = modbuf_load(ioptrap_path, &ioptrap_size))) + {return -1;} + + if (!(pcsx2hostfs_mod = modbuf_load(pcsx2hostfs_path, &pcsx2hostfs_size))) + return -1; + } + + else + { + dbgscr_printf("Using Cached Modules\n"); + } + return 0; +} +#endif + +//////////////////////////////////////////////////////////////////////// +// Load all the irx modules we need, according to 'boot mode' +static void +loadModules(void) +{ + int ret; +#ifdef USE_CACHED_CFG + int i,t; +#endif + + dbgscr_printf("loadModules \n"); + +#ifdef USE_CACHED_CFG + if(if_conf_len==0) + { +#endif + if ((boot == B_MC) || (boot == B_CC)) + { + pkoLoadModule("rom0:SIO2MAN", 0, NULL); + pkoLoadModule("rom0:MCMAN", 0, NULL); + pkoLoadModule("rom0:MCSERV", 0, NULL); + } +#ifdef USE_CACHED_CFG + return; + } +#endif + +#ifdef BUILTIN_IRXS + _binary_ioptrap_irx_size = _binary_ioptrap_irx_end - _binary_ioptrap_irx_start; + _binary_pcsx2hostfs_irx_size = _binary_pcsx2hostfs_irx_end - _binary_pcsx2hostfs_irx_start; + +#ifdef USE_CACHED_CFG + if(if_conf_len==0) + { + getIpConfig(); + } + else + { + i=0; + scr_printf("Net config: "); + for (t = 0, i = 0; t < 3; t++) { + scr_printf("%s ", &if_conf[i]); + i += strlen(&if_conf[i]) + 1; + } + scr_printf("\n"); + } + +#else + getIpConfig(); +#endif + + dbgscr_printf("Exec ioptrap module. (%x,%d) ", (unsigned int)_binary_ioptrap_irx_start, _binary_ioptrap_irx_size); + SifExecModuleBuffer(_binary_ioptrap_irx_start, _binary_ioptrap_irx_size, 0, NULL,&ret); + dbgscr_printf("[%d] returned\n", ret); + dbgscr_printf("Exec pcsx2hostfs module. (%x,%d) ", (unsigned int)_binary_pcsx2hostfs_irx_start, _binary_pcsx2hostfs_irx_size); + SifExecModuleBuffer(_binary_pcsx2hostfs_irx_start, _binary_pcsx2hostfs_irx_size, 0, NULL,&ret); + dbgscr_printf("[%d] returned\n", ret); + dbgscr_printf("All modules loaded on IOP.\n"); +#else + if (boot == B_HOST) { + + dbgscr_printf("Exec ioptrap module. (%x,%d) ", (u32)ioptrap_mod, ioptrap_size); + SifExecModuleBuffer(ioptrap_mod, ioptrap_size, 0, NULL,&ret); + dbgscr_printf("[%d] returned\n", ret); + dbgscr_printf("Exec pcsx2hostfs module. (%x,%d) ", (u32)pcsx2hostfs_mod, pcsx2hostfs_size); + SifExecModuleBuffer(pcsx2hostfs_mod, pcsx2hostfs_size, 0, NULL,&ret); + dbgscr_printf("[%d] returned\n", ret); + + + dbgscr_printf("All modules loaded on IOP.\n"); + } else { + getIpConfig(); + dbgscr_printf("Exec ioptrap module. "); + pkoLoadModule(ioptrap_path, 0, NULL); + dbgscr_printf("Exec pcsx2hostfs module. "); + pkoLoadModule(pcsx2hostfs_path, 0, NULL); + dbgscr_printf("All modules loaded on IOP. "); + } +#endif +} + +//////////////////////////////////////////////////////////////////////// +// C standard strrchr func.. +char *strrchr(const char *s, int i) +{ + const char *last = NULL; + char c = i; + + while (*s) { + if (*s == c) { + last = s; + } + s++; + } + + if (*s == c) { + last = s; + } + + return (char *) last; +} + +//////////////////////////////////////////////////////////////////////// +// Split path (argv[0]) at the last '/', '\' or ':' and initialise +// elfName (whole path & name to the elf, for example 'cdrom:\pukklink.elf') +// elfPath (path to where the elf was started, for example 'cdrom:\') +static void +setPathInfo(char *path) +{ + char *ptr; + + strncpy(elfName, path, 255); + strncpy(elfPath, path, 255); + elfName[255] = '\0'; + elfPath[255] = '\0'; + + + ptr = strrchr(elfPath, '/'); + if (ptr == NULL) { + ptr = strrchr(elfPath, '\\'); + if (ptr == NULL) { + ptr = strrchr(elfPath, ':'); + if (ptr == NULL) { + scr_printf("Did not find path (%s)!\n", path); + SleepThread(); + } + } + } + + ptr++; + *ptr = '\0'; + +#ifndef BUILTIN_IRXS + /* Paths to modules. */ + + sprintf(pcsx2hostfs_path, "%s%s", elfPath, "PS2LINK.IRX"); + sprintf(ioptrap_path, "%s%s", elfPath, "IOPTRAP.IRX"); + + if (boot == B_CD) { + strcat(ioptrap_path, ";1"); + strcat(pcsx2hostfs_path, ";1"); + } +#endif + + dbgscr_printf("path is %s\n", elfPath); +} + +//////////////////////////////////////////////////////////////////////// +// Clear user memory +void +wipeUserMem(void) +{ + int i; + // Whipe user mem + for (i = 0x100000; i < 0x2000000 ; i += 64) { + asm ( + "\tsq $0, 0(%0) \n" + "\tsq $0, 16(%0) \n" + "\tsq $0, 32(%0) \n" + "\tsq $0, 48(%0) \n" + :: "r" (i) ); + } +} + +//////////////////////////////////////////////////////////////////////// +// Clear user memory - Load High and Host version +void +wipeUserMemLoadHigh(void) +{ + int i; + // Whipe user mem, apart from last bit + for (i = 0x100000; i < 0x1F00000 ; i += 64) { + asm ( + "\tsq $0, 0(%0) \n" + "\tsq $0, 16(%0) \n" + "\tsq $0, 32(%0) \n" + "\tsq $0, 48(%0) \n" + :: "r" (i) ); + } +} + + +void +restartIOP() +{ + fioExit(); + SifExitIopHeap(); + SifLoadFileExit(); + SifExitRpc(); + + dbgscr_printf("reset iop\n"); + SifIopReset(imgcmd, 0); + while (SifIopSync()) ; + + dbgscr_printf("rpc init\n"); + SifInitRpc(0); + + scr_printf("Initializing...\n"); + sio_printf("Initializing...\n"); + sbv_patch_enable_lmb(); + sbv_patch_disable_prefix_check(); + +// SifLoadFileReset(); + dbgscr_printf("loading modules\n"); + loadModules(); +} + +#if HOOK_THREADS + +// we can spare 1k to allow a generous number of threads.. +#define MAX_MONITORED_THREADS 256 + +int _first_load = 1; + +int th_count; +int active_threads[MAX_MONITORED_THREADS]; + +void *addr_LoadExecPS2; +void *addr_CreateThread; +void *addr_DeleteThread; + +void InstallKernelHooks(void) +{ + // get the current address of each syscall we want to hook then replace the entry + // in the syscall table with that of our hook function. + + addr_LoadExecPS2 = GetSyscall(6); + SetSyscall(6, &Hook_LoadExecPS2); + + addr_CreateThread = GetSyscall(32); + SetSyscall(32, &Hook_CreateThread); + + addr_DeleteThread = GetSyscall(33); + SetSyscall(33, &Hook_DeleteThread); +} + +void RemoveKernelHooks(void) +{ + SetSyscall(6, addr_LoadExecPS2); + SetSyscall(32, addr_CreateThread); + SetSyscall(33, addr_DeleteThread); +} + +int Hook_LoadExecPS2(char *fname, int argc, char *argv[]) +{ + // kill any active threads + KillActiveThreads(); + + // remove our kernel hooks + RemoveKernelHooks(); + + // call the real LoadExecPS2 handler, this will never return + return(((int (*)(char *, int, char **)) (addr_LoadExecPS2))(fname, argc, argv)); +} + +int Hook_CreateThread(ee_thread_t *thread_p) +{ + int i; + for(i = 0; i < MAX_MONITORED_THREADS; i++) + { + if(active_threads[i] < 0) { break; } + } + + if(i >= MAX_MONITORED_THREADS) { return(-1); } + + // call the real CreateThread handler, saving the thread id in our list + return(active_threads[i] = ((int (*)(ee_thread_t *)) (addr_CreateThread))(thread_p)); +} + +int Hook_DeleteThread(int th_id) +{ + int i; + + for(i = 0; i < MAX_MONITORED_THREADS; i++) + { + if(active_threads[i] == th_id) + { + // remove the entry from the active thread list. + active_threads[i] = -1; + break; + } + } + + // call the real DeleteThread handler + return(((int (*)(int)) (addr_DeleteThread))(th_id)); +} + +// kill all threads created and not deleted since PS2LINK.ELF started except for the calling thread. +void KillActiveThreads(void) +{ + int my_id = GetThreadId(); + int i; + + for(i = 0; i < MAX_MONITORED_THREADS; i++) + { + if((active_threads[i] >= 0) && (active_threads[i] != my_id)) + { + TerminateThread(active_threads[i]); + DeleteThread(active_threads[i]); + active_threads[i] = -1; + } + } +} + +void ResetActiveThreads(void) +{ + int i; + for(i = 0; i < MAX_MONITORED_THREADS; i++) { active_threads[i] = -1; } +} +#endif + +extern void _start(void); +extern int _end; + +//////////////////////////////////////////////////////////////////////// +int +main(int argc, char *argv[]) +{ + // int ret; + char *bootPath; + + init_scr(); + scr_printf(WELCOME_STRING); +#ifdef _LOADHIGHVER + scr_printf("Highload version\n"); +#endif + +#ifdef SCREENSHOTS + scr_printf("Screenshot capable\n"); +#endif + + scr_printf("pcsx2hostfs loaded at 0x%08X-0x%08X\n", ((u32) _start) - 8, (u32) &_end); + + installExceptionHandlers(); + +#if HOOK_THREADS + if(_first_load) + { + _first_load = 0; + InstallKernelHooks(); + } + + ResetActiveThreads(); +#endif + + // argc == 0 usually means naplink.. + if (argc == 0) { + bootPath = "host:"; + } + // reload1 usually gives an argc > 60000 (yea, this is kinda a hack..) + else if (argc != 1) { + bootPath = "mc0:/BWLINUX/"; + } + else { + bootPath = argv[0]; + } + + SifInitRpc(0); + dbgscr_printf("Checking argv\n"); + boot = 0; + + setPathInfo(bootPath); + + if(!strncmp(bootPath, "mc", strlen("mc"))) { + // Booting from my mc + scr_printf("Booting from mc dir (%s)\n", bootPath); + boot = B_MC; + } + else if(!strncmp(bootPath, "host", strlen("host"))) { + // Host + scr_printf("Booting from host (%s)\n", bootPath); + boot = B_HOST; + } + else if(!strncmp(bootPath, "rom0:OSDSYS", strlen("rom0:OSDSYS"))) { + // From CC's firmware + scr_printf("Booting as CC firmware\n"); + boot = B_CC; + } + else { + // Unknown + scr_printf("Booting from unrecognized place %s\n", bootPath); + boot = B_UNKN; + } + +#ifdef USE_CACHED_CFG + if(if_conf_len==0) + { + scr_printf("Initial boot, will load config then reset\n"); + if(boot == B_MC || boot == B_CC) + restartIOP(); + + getIpConfig(); + + } + else + { + scr_printf("Using cached config\n"); + } +#endif + + // System initalisation +#ifndef BUILTIN_IRXS + if (boot == B_HOST) { + if (loadHostModBuffers() < 0) { + dbgscr_printf("Unable to load modules from host:!\n"); + SleepThread(); + } + } +#endif + + restartIOP(); + +// CLEARSPU seems to cause exceptions in the Multi_Thread_Manager module. +// I suspect this is caused by the interrupt handlers or some such. +/* + dbgscr_printf("clearing spu\n"); + if (SifLoadModule("rom0:CLEARSPU", 0, NULL)<0) + { + scr_printf("rom0:CLEARSPU failed\n"); + sio_printf("rom0:CLEARSPU failed\n"); + } +*/ + + // get extra config + if(load_extra_conf) + { + dbgscr_printf("getting extra config\n"); + getExtraConfig(); + } + + scr_printf("Ready\n"); + sio_printf("Ready\n"); + nprintf("Ready\n"); + + char test[101]; + int fd = open("host:/I:/test_atom.xml", O_RDONLY); + if(fd>=0) + { + int rd = read(fd,test,100); + close(fd); + + if(rd>0) + test[rd]=0; + test[100]=0; + + nprintf("Reading returned %d: %s",rd,test); + } + else printf("Test failed."); + +// SleepThread(); + ExitDeleteThread(); + return 0; +} diff --git a/pcsx2hostfs/ee/ps2regs.h b/pcsx2hostfs/ee/ps2regs.h new file mode 100644 index 0000000000..02dc534547 --- /dev/null +++ b/pcsx2hostfs/ee/ps2regs.h @@ -0,0 +1,286 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef PS2REGS_H +#define PS2REGS_H + +/* TIMERS */ +#define T0_COUNT *((volatile unsigned int *)0x10000000) +#define T0_MODE *((volatile unsigned short *)0x10000010) +#define T0_COMP *((volatile unsigned short *)0x10000020) +#define T0_HOLD *((volatile unsigned short *)0x10000030) + +#define T1_COUNT *((volatile unsigned int *)0x10000800) +#define T1_MODE *((volatile unsigned short *)0x10000810) +#define T1_COMP *((volatile unsigned short *)0x10000820) +#define T1_HOLD *((volatile unsigned short *)0x10000830) + +#define T2_COUNT *((volatile unsigned int *)0x10001000) +#define T2_MODE *((volatile unsigned short *)0x10001010) +#define T2_COMP *((volatile unsigned short *)0x10001020) + +#define T3_COUNT *((volatile unsigned int *)0x10001800) +#define T3_MODE *((volatile unsigned short *)0x10001810) +#define T3_COMP *((volatile unsigned short *)0x10001820) + +/* IPU */ +#define IPU_CMD *((volatile unsigned long *)0x10002000) +#define IPU_CTRL *((volatile unsigned int *)0x10002010) +#define IPU_BP *((volatile unsigned int *)0x10002020) +#define IPU_TOP *((volatile unsigned long *)0x10002030) + +/* GIF */ +#define GIF_CTRL *((volatile unsigned int *)0x10003000) +#define GIF_MODE *((volatile unsigned int *)0x10003010) +#define GIF_STAT *((volatile unsigned int *)0x10003020) + +#define GIF_TAG0 *((volatile unsigned int *)0x10003040) +#define GIF_TAG1 *((volatile unsigned int *)0x10003050) +#define GIF_TAG2 *((volatile unsigned int *)0x10003060) +#define GIF_TAG3 *((volatile unsigned int *)0x10003070) +#define GIF_CNT *((volatile unsigned int *)0x10003080) +#define GIF_P3CNT *((volatile unsigned int *)0x10003090) +#define GIF_P3TAG *((volatile unsigned int *)0x100030a0) + +/* VIF */ +#define VIF0_STAT *((volatile unsigned int *)0x10003800) +#define VIF0_FBRST *((volatile unsigned int *)0x10003810) +#define VIF0_ERR *((volatile unsigned int *)0x10003820) +#define VIF0_MARK *((volatile unsigned int *)0x10003830) +#define VIF0_CYCLE *((volatile unsigned int *)0x10003840) +#define VIF0_MODE *((volatile unsigned int *)0x10003850) +#define VIF0_NUM *((volatile unsigned int *)0x10003860) +#define VIF0_MASK *((volatile unsigned int *)0x10003870) +#define VIF0_CODE *((volatile unsigned int *)0x10003880) +#define VIF0_ITOPS *((volatile unsigned int *)0x10003890) + +#define VIF0_ITOP *((volatile unsigned int *)0x100038d0) + +#define VIF0_R0 *((volatile unsigned int *)0x10003900) +#define VIF0_R1 *((volatile unsigned int *)0x10003910) +#define VIF0_R2 *((volatile unsigned int *)0x10003920) +#define VIF0_R3 *((volatile unsigned int *)0x10003930) +#define VIF0_C0 *((volatile unsigned int *)0x10003940) +#define VIF0_C1 *((volatile unsigned int *)0x10003950) +#define VIF0_C2 *((volatile unsigned int *)0x10003960) +#define VIF0_C3 *((volatile unsigned int *)0x10003970) + +#define VIF1_STAT *((volatile unsigned int *)0x10003c00) +#define VIF1_FBRST *((volatile unsigned int *)0x10003c10) +#define VIF1_ERR *((volatile unsigned int *)0x10003c20) +#define VIF1_MARK *((volatile unsigned int *)0x10003c30) +#define VIF1_CYCLE *((volatile unsigned int *)0x10003c40) +#define VIF1_MODE *((volatile unsigned int *)0x10003c50) +#define VIF1_NUM *((volatile unsigned int *)0x10003c60) +#define VIF1_MASK *((volatile unsigned int *)0x10003c70) +#define VIF1_CODE *((volatile unsigned int *)0x10003c80) +#define VIF1_ITOPS *((volatile unsigned int *)0x10003c90) +#define VIF1_BASE *((volatile unsigned int *)0x10003ca0) +#define VIF1_OFST *((volatile unsigned int *)0x10003cb0) +#define VIF1_TOPS *((volatile unsigned int *)0x10003cc0) +#define VIF1_ITOP *((volatile unsigned int *)0x10003cd0) +#define VIF1_TOP *((volatile unsigned int *)0x10003ce0) + +#define VIF1_R0 *((volatile unsigned int *)0x10003d00) +#define VIF1_R1 *((volatile unsigned int *)0x10003d10) +#define VIF1_R2 *((volatile unsigned int *)0x10003d20) +#define VIF1_R3 *((volatile unsigned int *)0x10003d30) +#define VIF1_C0 *((volatile unsigned int *)0x10003d40) +#define VIF1_C1 *((volatile unsigned int *)0x10003d50) +#define VIF1_C2 *((volatile unsigned int *)0x10003d60) +#define VIF1_C3 *((volatile unsigned int *)0x10003d70) + +/* FIFO */ +//#define VIF0_FIFO(write) *((volatile unsigned int128 *)0x10004000) +//#define VIF1_FIFO(read/write) *((volatile unsigned int128 *)0x10005000) +//#define GIF_FIFO(write) *((volatile unsigned int128 *)0x10006000) +//#define IPU_out_FIFO(read) *((volatile unsigned int128 *)0x10007000) +//#define IPU_in_FIFO(write) *((volatile unsigned int128 *)0x10007010) + +/* DMAC */ +#define CHCR 0x00 +#define MADR 0x04 +#define QWC 0x08 +#define TADR 0x0C +#define ASR0 0x10 +#define ASR1 0x14 + +#define DMA0 ((volatile unsigned int *)0x10008000) +#define DMA1 ((volatile unsigned int *)0x10009000) +#define DMA2 ((volatile unsigned int *)0x1000a000) +#define DMA3 ((volatile unsigned int *)0x1000b000) +#define DMA4 ((volatile unsigned int *)0x1000b400) +#define DMA5 ((volatile unsigned int *)0x1000c000) +#define DMA6 ((volatile unsigned int *)0x1000c400) +#define DMA7 ((volatile unsigned int *)0x1000c800) +#define DMA8 ((volatile unsigned int *)0x1000d000) +#define DMA9 ((volatile unsigned int *)0x1000d400) + +#define D0_CHCR *((volatile unsigned int *)0x10008000) +#define D0_MADR *((volatile unsigned int *)0x10008010) +#define D0_QWC *((volatile unsigned int *)0x10008020) +#define D0_TADR *((volatile unsigned int *)0x10008030) +#define D0_ASR0 *((volatile unsigned int *)0x10008040) +#define D0_ASR1 *((volatile unsigned int *)0x10008050) + +#define D1_CHCR *((volatile unsigned int *)0x10009000) +#define D1_MADR *((volatile unsigned int *)0x10009010) +#define D1_QWC *((volatile unsigned int *)0x10009020) +#define D1_TADR *((volatile unsigned int *)0x10009030) +#define D1_ASR0 *((volatile unsigned int *)0x10009040) +#define D1_ASR1 *((volatile unsigned int *)0x10009050) + +#define D2_CHCR *((volatile unsigned int *)0x1000a000) +#define D2_MADR *((volatile unsigned int *)0x1000a010) +#define D2_QWC *((volatile unsigned int *)0x1000a020) +#define D2_TADR *((volatile unsigned int *)0x1000a030) +#define D2_ASR0 *((volatile unsigned int *)0x1000a040) +#define D2_ASR1 *((volatile unsigned int *)0x1000a050) + +#define D3_CHCR *((volatile unsigned int *)0x1000b000) +#define D3_MADR *((volatile unsigned int *)0x1000b010) +#define D3_QWC *((volatile unsigned int *)0x1000b020) + +#define D4_CHCR *((volatile unsigned int *)0x1000b400) +#define D4_MADR *((volatile unsigned int *)0x1000b410) +#define D4_QWC *((volatile unsigned int *)0x1000b420) +#define D4_TADR *((volatile unsigned int *)0x1000b430) + +#define D5_CHCR *((volatile unsigned int *)0x1000c000) +#define D5_MADR *((volatile unsigned int *)0x1000c010) +#define D5_QWC *((volatile unsigned int *)0x1000c020) + +#define D6_CHCR *((volatile unsigned int *)0x1000c400) +#define D6_MADR *((volatile unsigned int *)0x1000c410) +#define D6_QWC *((volatile unsigned int *)0x1000c420) + +#define D7_CHCR *((volatile unsigned int *)0x1000c800) +#define D7_MADR *((volatile unsigned int *)0x1000c810) +#define D7_QWC *((volatile unsigned int *)0x1000c820) + +#define D8_CHCR *((volatile unsigned int *)0x1000d000) +#define D8_MADR *((volatile unsigned int *)0x1000d010) +#define D8_QWC *((volatile unsigned int *)0x1000d020) + +#define D8_SADR *((volatile unsigned int *)0x1000d080) + +#define D9_CHCR *((volatile unsigned int *)0x1000d400) +#define D9_MADR *((volatile unsigned int *)0x1000d410) +#define D9_QWC *((volatile unsigned int *)0x1000d420) +#define D9_TADR *((volatile unsigned int *)0x1000d430) + +#define D9_SADR *((volatile unsigned int *)0x1000d480) + +/* DMAC */ +#define D_CTRL *((volatile unsigned int *)0x1000e000) +#define D_STAT *((volatile unsigned int *)0x1000e010) +#define D_PCR *((volatile unsigned int *)0x1000e020) +#define D_SQWC *((volatile unsigned int *)0x1000e030) +#define D_RBSR *((volatile unsigned int *)0x1000e040) +#define D_RBOR *((volatile unsigned int *)0x1000e050) +#define D_STADR *((volatile unsigned int *)0x1000e060) +/* INTC */ +//#define I_STAT 0x1000f000 +//#define I_MASK 0x1000f010 + +/* TOOL putchar */ +// .byte KPUTCHAR (0x1000f180) +/* SIF */ +// #define SB_SMFLG (0x1000f230) + +/* DMAC */ +#define D_ENABLER *((volatile unsigned int *)0x1000f520) +#define D_ENABLEW *((volatile unsigned int *)0x1000f590) + +/* VU Mem */ +#define VUMicroMem0 *((volatile unsigned int *)0x11000000) +#define VUMem0 *((volatile unsigned int *)0x11004000) +#define VUMicroMem1 *((volatile unsigned int *)0x11008000) +#define VUMem1 *((volatile unsigned int *)0x1100c000) + +/* GS Privileged */ +#define GS_PMODE *((volatile unsigned long *)0x12000000) +#define GS_SMODE1 *((volatile unsigned long *)0x12000010) +#define GS_SMODE2 *((volatile unsigned long *)0x12000020) +#define GS_SRFSH *((volatile unsigned long *)0x12000030) +#define GS_SYNCH1 *((volatile unsigned long *)0x12000040) +#define GS_SYNCH2 *((volatile unsigned long *)0x12000050) +#define GS_SYNCV *((volatile unsigned long *)0x12000060) +#define GS_DISPFB1 *((volatile unsigned long *)0x12000070) +#define GS_DISPLAY1 *((volatile unsigned long *)0x12000080) +#define GS_DISPFB2 *((volatile unsigned long *)0x12000090) +#define GS_DISPLAY2 *((volatile unsigned long *)0x120000a0) +#define GS_EXTBUF *((volatile unsigned long *)0x120000b0) +#define GS_EXTDATA *((volatile unsigned long *)0x120000c0) +#define GS_EXTWRITE *((volatile unsigned long *)0x120000d0) +#define GS_BGCOLOR *((volatile unsigned long *)0x120000e0) + +#define GS_CSR *((volatile unsigned long *)0x12001000) +#define GS_IMR *((volatile unsigned long *)0x12001010) + +#define GS_BUSDIR *((volatile unsigned long *)0x12001040) + +#define GS_SIGLBLID *((volatile unsigned long *)0x12001080) + +/* GS General */ +#define GS_PRIM 0x00 +#define GS_RGBAQ 0x01 +#define GS_ST 0x02 +#define GS_UV 0x03 +#define GS_XYZF2 0x04 +#define GS_XYZ2 0x05 +#define GS_TEX0_1 0x06 +#define GS_TEX0_2 0x07 +#define GS_CLAMP_1 0x08 +#define GS_CLAMP_2 0x09 +#define GS_FOG 0x0a +#define GS_XYZF3 0x0c +#define GS_XYZ3 0x0d +#define GS_AD 0x0e +#define GS_TEX1_1 0x14 +#define GS_TEX1_2 0x15 +#define GS_TEX2_1 0x16 +#define GS_TEX2_2 0x17 +#define GS_XYOFFSET_1 0x18 +#define GS_XYOFFSET_2 0x19 +#define GS_PRMODECONT 0x1a +#define GS_PRMODE 0x1b +#define GS_TEXCLUT 0x1c +#define GS_SCANMSK 0x22 +#define GS_MIPTBP1_1 0x34 +#define GS_MIPTBP1_2 0x35 +#define GS_MIPTBP2_1 0x36 +#define GS_MIPTBP2_2 0x37 +#define GS_TEXA 0x3b +#define GS_FOGCOL 0x3d +#define GS_TEXFLUSH 0x3f +#define GS_SCISSOR_1 0x40 +#define GS_SCISSOR_2 0x41 +#define GS_ALPHA_1 0x42 +#define GS_ALPHA_2 0x43 +#define GS_DIMX 0x44 +#define GS_DTHE 0x45 +#define GS_COLCLAMP 0x46 +#define GS_TEST_1 0x47 +#define GS_TEST_2 0x48 +#define GS_PABE 0x49 +#define GS_FBA_1 0x4a +#define GS_FBA_2 0x4b +#define GS_FRAME_1 0x4c +#define GS_FRAME_2 0x4d +#define GS_ZBUF_1 0x4e +#define GS_ZBUF_2 0x4f +#define GS_BITBLTBUF 0x50 +#define GS_TRXPOS 0x51 +#define GS_TRXREG 0x52 +#define GS_TRXDIR 0x53 +#define GS_HWREG 0x54 +#define GS_SIGNAL 0x60 +#define GS_FINISH 0x61 +#define GS_LABEL 0x62 + +#endif diff --git a/pcsx2hostfs/ee/r5900_regs.h b/pcsx2hostfs/ee/r5900_regs.h new file mode 100644 index 0000000000..79da322ca2 --- /dev/null +++ b/pcsx2hostfs/ee/r5900_regs.h @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------ +// File: regs.h +// Author: Tony Saveski, t_saveski@yahoo.com +// Notes: Playstation 2 Register Definitions +//------------------------------------------------------------------------ +#ifndef R5900_REGS_H +#define R5900_REGS_H + +// MIPS CPU Registsers +#define zero $0 // Always 0 +#define at $1 // Assembler temporary +#define v0 $2 // Function return +#define v1 $3 // +#define a0 $4 // Function arguments +#define a1 $5 +#define a2 $6 +#define a3 $7 +#define t0 $8 // Temporaries. No need +#define t1 $9 // to preserve in your +#define t2 $10 // functions. +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 +#define s0 $16 // Saved Temporaries. +#define s1 $17 // Make sure to restore +#define s2 $18 // to original value +#define s3 $19 // if your function +#define s4 $20 // changes their value. +#define s5 $21 +#define s6 $22 +#define s7 $23 +#define t8 $24 // More Temporaries. +#define t9 $25 +#define k0 $26 // Reserved for Kernel +#define k1 $27 +#define gp $28 // Global Pointer +#define sp $29 // Stack Pointer +#define fp $30 // Frame Pointer +#define ra $31 // Function Return Address + +// COP0 +#define Index $0 // Index into the TLB array +#define Random $1 // Randomly generated index into the TLB array +#define EntryLo0 $2 // Low-order portion of the TLB entry for.. +#define EntryLo1 $3 // Low-order portion of the TLB entry for +#define Context $4 // Pointer to page table entry in memory +#define PageMask $5 // Control for variable page size in TLB entries +#define Wired $6 // Controls the number of fixed ("wired") TLB entries +#define BadVAddr $8 // Address for the most recent address-related exception +#define Count $9 // Processor cycle count +#define EntryHi $10 // High-order portion of the TLB entry +#define Compare $11 // Timer interrupt control +#define Status $12 // Processor status and control +#define Cause $13 // Cause of last general exception +#define EPC $14 // Program counter at last exception +#define PRId $15 // Processor identification and revision +#define Config $16 // Configuration register +#define LLAddr $17 // Load linked address +#define WatchLo $18 // Watchpoint address Section 6.25 on +#define WatchHi $19 // Watchpoint control +#define Debug $23 // EJTAG Debug register +#define DEPC $24 // Program counter at last EJTAG debug exception +#define PerfCnt $25 // Performance counter interface +#define ErrCtl $26 // Parity/ECC error control and status +#define CacheErr $27 // Cache parity error control and status +#define TagLo $28 // Low-order portion of cache tag interface +#define TagHi $29 // High-order portion of cache tag interface +#define ErrorPC $30 // Program counter at last error +#define DEASVE $31 // EJTAG debug exception save register + +#endif // R5900_REGS_H diff --git a/pcsx2hostfs/include/byteorder.h b/pcsx2hostfs/include/byteorder.h new file mode 100644 index 0000000000..c1a35e0794 --- /dev/null +++ b/pcsx2hostfs/include/byteorder.h @@ -0,0 +1,42 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef BYTEORDER_H +#define BYTEORDER_H + +#include + +#ifdef BIG_ENDIAN +inline unsigned int ntohl(x) { return x; } +inline unsigned short ntohs(x) { return x; } +inline unsigned int ntohl(x) { return x; } +inline unsigned short ntohs(x) { return x; } +#else +// LITTLE_ENDIAN +inline unsigned int +htonl(unsigned int x) +{ + return ((x & 0xff) << 24 ) | + ((x & 0xff00) << 8 ) | + ((x & 0xff0000) >> 8 ) | + ((x & 0xff000000) >> 24 ); +} + +inline unsigned short +htons(unsigned short x) +{ + return ((x & 0xff) << 8 ) | ((x & 0xff00) >> 8 ); +} + +#define ntohl htonl +#define ntohs htons + +#endif + +#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->s_addr = htonl(((u32)(a & 0xff) << 24) | ((u32)(b & 0xff) << 16 ) | ((u32)(c & 0xff) << 8) | (u32)(d & 0xff)) + +#endif /* BYTEORDER_H */ diff --git a/pcsx2hostfs/include/hostlink.h b/pcsx2hostfs/include/hostlink.h new file mode 100644 index 0000000000..4d40bd8f98 --- /dev/null +++ b/pcsx2hostfs/include/hostlink.h @@ -0,0 +1,22 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#define PS2E_FIO_OPEN_CMD 0xcc2e0101 +#define PS2E_FIO_CLOSE_CMD 0xcc2e0102 +#define PS2E_FIO_READ_CMD 0xcc2e0103 +#define PS2E_FIO_WRITE_CMD 0xcc2e0104 +#define PS2E_FIO_LSEEK_CMD 0xcc2e0105 +#define PS2E_FIO_OPENDIR_CMD 0xcc2e0106 +#define PS2E_FIO_CLOSEDIR_CMD 0xcc2e0107 +#define PS2E_FIO_READDIR_CMD 0xcc2e0108 +#define PS2E_FIO_REMOVE_CMD 0xcc2e0109 +#define PS2E_FIO_MKDIR_CMD 0xcc2e010a +#define PS2E_FIO_RMDIR_CMD 0xcc2e010b + +#define PS2E_FIO_PRINTF_CMD 0xcc2e0201 + +#define PS2E_FIO_MAX_PATH 256 diff --git a/pcsx2hostfs/iop/Makefile b/pcsx2hostfs/iop/Makefile new file mode 100644 index 0000000000..1ecfe2a038 --- /dev/null +++ b/pcsx2hostfs/iop/Makefile @@ -0,0 +1,43 @@ +# _____ ___ ____ +# ____| | ____| PSX2 OpenSource Project +# | ___| |____ (C)2002, David Ryan ( Oobles@hotmail.com ) +# ------------------------------------------------------------------------ + +# Generated automatically from Makefile.in by configure. +#.SUFFIXES: .S .c .o .s .elf .irx + +IOP_BIN = pcsx2hostfs.irx +IOP_OBJS = net_fsys.o net_fio.o ps2link.o tty.o nprintf.o excepHandler.o imports.o + +IOP_INCS += -I../include +IOP_LIBS += +IOP_LDFLAGS += +IOP_CFLAGS += -Wall + +# Enable zero-copy on fileio writes. +ifeq ($(ZEROCOPY),1) +IOP_CFLAGS += -DZEROCOPY +endif + +# Enable debug mode +ifeq ($(DEBUG),1) +IOP_CFLAGS += -DDEBUG +endif + +ifeq ($(PWOFFONRESET),1) +IOP_CFLAGS += -DPWOFFONRESET +endif + +# Enable screenshot functionality +ifeq ($(SCREENSHOTS),1) +IOP_CFLAGS += -DSCREENSHOTS +endif + + +all: $(IOP_BIN) + +clean: + -rm -f $(IOP_OBJS) $(IOP_BIN) + +include $(PS2SDK)/Defs.make +include Rules.make diff --git a/pcsx2hostfs/iop/Rules.make b/pcsx2hostfs/iop/Rules.make new file mode 100644 index 0000000000..d3be71609d --- /dev/null +++ b/pcsx2hostfs/iop/Rules.make @@ -0,0 +1,54 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004. +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + + +IOP_CC_VERSION := $(shell $(IOP_CC) -v 2>&1 | sed -n 's/^.*version //p') + +ASFLAGS_TARGET = -mcpu=r3000 + +ifeq ($(IOP_CC_VERSION),3.2.2) +CFLAGS_TARGET = -miop +ASFLAGS_TARGET = -march=r3000 +LDFLAGS_TARGET = -miop +endif + +IOP_INCS := $(IOP_INCS) -I$(PS2SDK)/iop/include -I$(PS2SDK)/common/include + +IOP_CFLAGS := $(CFLAGS_TARGET) -O2 -G0 -c $(IOP_INCS) $(IOP_CFLAGS) +IOP_ASFLAGS := $(ASFLAGS_TARGET) -EL -G0 $(IOP_ASFLAGS) +IOP_LDFLAGS := $(LDFLAGS_TARGET) -nostdlib $(IOP_LDFLAGS) + +# Externally defined variables: IOP_BIN, IOP_OBJS, IOP_LIB + +%.o : %.c + $(IOP_CC) $(IOP_CFLAGS) $< -o $@ + +%.o : %.s + $(IOP_AS) $(IOP_ASFLAGS) $< -o $@ + +# A rule to build imports.lst. +%.o : %.lst + echo "#include \"irx_imports.h\"" > build-imports.c + cat $< >> build-imports.c + $(IOP_CC) $(IOP_CFLAGS) build-imports.c -o $@ + -rm -f build-imports.c + +# A rule to build exports.tab. +%.o : %.tab + echo "#include \"irx.h\"" > build-exports.c + cat $< >> build-exports.c + $(IOP_CC) $(IOP_CFLAGS) build-exports.c -o $@ + -rm -f build-exports.c + + +$(IOP_BIN) : $(IOP_OBJS) + $(IOP_CC) $(IOP_LDFLAGS) -o $(IOP_BIN) $(IOP_OBJS) $(IOP_LIBS) + +$(IOP_LIB) : $(IOP_OBJS) + $(IOP_AR) cru $(IOP_LIB) $(IOP_OBJS) + diff --git a/pcsx2hostfs/iop/excepHandler.c b/pcsx2hostfs/iop/excepHandler.c new file mode 100644 index 0000000000..a3a7044da2 --- /dev/null +++ b/pcsx2hostfs/iop/excepHandler.c @@ -0,0 +1,119 @@ +/********************************************************************* + * Copyright (C) 2004 Lukasz Bruun (mail@lukasz.dk) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include "irx_imports.h" +#include "hostlink.h" + +#define BUFFER_SIZE sizeof(exception_frame_t)+4+4+128 + +u32 excep_buffer[BUFFER_SIZE/4] __attribute__ ((aligned(16))); + +extern unsigned int pcsx2fioSendSifCmd(unsigned int cmd, void *src, unsigned int len); // lazy fix :) +extern int excepscrdump; + +#define PKO_RPC_IOPEXCEP 12 + +// taken from smod by mrbrown, only use one function, didn't wanna include another irx. + +/* Module info entry. */ +typedef struct _smod_mod_info { + struct _smod_mod_info *next; + u8 *name; + u16 version; + u16 newflags; /* For modload shipped with games. */ + u16 id; + u16 flags; /* I believe this is where flags are kept for BIOS versions. */ + u32 entry; /* _start */ + u32 gp; + u32 text_start; + u32 text_size; + u32 data_size; + u32 bss_size; + u32 unused1; + u32 unused2; +} smod_mod_info_t; + + +smod_mod_info_t *smod_get_next_mod(smod_mod_info_t *cur_mod) +{ + /* If cur_mod is 0, return the head of the list (IOP address 0x800). */ + if (!cur_mod) { + return (smod_mod_info_t *)0x800; + } else { + if (!cur_mod->next) + return 0; + else + return cur_mod->next; + } + return 0; +} + +char* ExceptionGetModuleName(u32 epc, u32* r_epc) +{ + smod_mod_info_t *mod_info = 0; + + while((mod_info = smod_get_next_mod(mod_info)) != 0) + { + if((epc >= mod_info->text_start) && (epc <= (mod_info->text_start+mod_info->text_size))) + { + if(r_epc) + *r_epc = epc - mod_info->text_start; + + return mod_info->name; + } + } + + return 0; +} + +static void excep_handler2(exception_frame_t *frame) +{ + u32 r_epc; // relative epc + char *module_name; + u32 len; + u32* buffer = excep_buffer; + + module_name = ExceptionGetModuleName(frame->epc, &r_epc); + + len = strlen(module_name); + + + buffer[0] = 0x0d02beba; // reverse engineering.. + buffer++; + + memcpy(buffer, frame, BUFFER_SIZE); + buffer+= (sizeof(exception_frame_t)/4); + + buffer[0] = r_epc; + buffer[1] = len; + buffer+=2; + memcpy(buffer, module_name, len+1); + + //pcsx2fioSendSifCmd(PKO_RPC_IOPEXCEP, excep_buffer, BUFFER_SIZE); +} + + +static void excep_handler(exception_type_t type, exception_frame_t *frame) +{ + excep_handler2(frame); // Don't know why this works, but keeps iop alive. +} + + +//////////////////////////////////////////////////////////////////////// +// Installs iop exception handlers for the 'usual' exceptions.. +void installExceptionHandlers(void) +{ + s32 i; + + for(i=1; i < 8; i++) + set_exception_handler(i, excep_handler); + + for(i=10; i < 13; i++) + set_exception_handler(i, excep_handler); + +} diff --git a/pcsx2hostfs/iop/excepHandler.h b/pcsx2hostfs/iop/excepHandler.h new file mode 100644 index 0000000000..603bff8a53 --- /dev/null +++ b/pcsx2hostfs/iop/excepHandler.h @@ -0,0 +1,13 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef _EXCEPTION_H_ +#define _EXCEPTION_H_ + +void installExceptionHandlers(void); + +#endif diff --git a/pcsx2hostfs/iop/imports.lst b/pcsx2hostfs/iop/imports.lst new file mode 100644 index 0000000000..5b34397438 --- /dev/null +++ b/pcsx2hostfs/iop/imports.lst @@ -0,0 +1,63 @@ + +stdio_IMPORTS_start +I_printf +stdio_IMPORTS_end + +sysclib_IMPORTS_start +I_memset +I_memcpy +I_strlen +I_strncpy +I_strncmp +sysclib_IMPORTS_end + +thsemap_IMPORTS_start +I_CreateSema +I_SignalSema +I_WaitSema +I_DeleteSema +thsemap_IMPORTS_end + +thbase_IMPORTS_start +I_CreateThread +I_DeleteThread +I_StartThread +I_GetThreadId +I_ExitDeleteThread +thbase_IMPORTS_end + +ioman_IMPORTS_start +I_open +I_close +I_AddDrv +I_DelDrv +ioman_IMPORTS_end + +sifcmd_IMPORTS_start +I_sceSifInitRpc +I_sceSifSetRpcQueue +I_sceSifRegisterRpc +I_sceSifRpcLoop +sifcmd_IMPORTS_end + +sifman_IMPORTS_start +I_sceSifSetDma +sifman_IMPORTS_end + +intrman_IMPORTS_start +I_CpuEnableIntr +I_CpuSuspendIntr +I_CpuResumeIntr +intrman_IMPORTS_end + +loadcore_IMPORTS_start +I_FlushDcache +loadcore_IMPORTS_end + +modload_IMPORTS_start +I_LoadStartModule +modload_IMPORTS_end + +ioptrap_IMPORTS_start +I_set_exception_handler +ioptrap_IMPORTS_end diff --git a/pcsx2hostfs/iop/irx_imports.h b/pcsx2hostfs/iop/irx_imports.h new file mode 100644 index 0000000000..d40030c993 --- /dev/null +++ b/pcsx2hostfs/iop/irx_imports.h @@ -0,0 +1,28 @@ +/* + * irx_imports.h - Defines all IRX imports. + * + * Copyright (c) 2003 Marcus R. Brown + * + * See the file LICENSE included with this distribution for licensing terms. + */ + +#ifndef IOP_IRX_IMPORTS_H +#define IOP_IRX_IMPORTS_H + +#include "irx.h" + + +/* Please keep these in alphabetical order! */ +#include "intrman.h" +#include "ioman.h" +#include "ioptrap.h" +#include "loadcore.h" +#include "modload.h" +#include "sifcmd.h" +#include "sifman.h" +#include "stdio.h" +#include "sysclib.h" +#include "thbase.h" +#include "thsemap.h" + +#endif /* IOP_IRX_IMPORTS_H */ diff --git a/pcsx2hostfs/iop/mipsirx.x b/pcsx2hostfs/iop/mipsirx.x new file mode 100644 index 0000000000..d358cca6f6 --- /dev/null +++ b/pcsx2hostfs/iop/mipsirx.x @@ -0,0 +1,80 @@ +/* Link script for PlayStation 2 IRXs + * Written by Douglas C. Knight + */ +OUTPUT_FORMAT("elf32-littlemips") + SEARCH_DIR(/home/karmix/local/iop/lib); +ENTRY(_start) +SECTIONS +{ + /* This is the .iopmod section for the IRX, it contains + information that the IOP uses when loading the IRX. + This section is placed in its own segment. */ + .iopmod : { + /* The linker will replace this first LONG with a pointer + to _irx_id if the symbol has been defined. */ + LONG (0xffffffff) ; + LONG (_start) ; + LONG (_gp) ; + LONG (_text_size) ; + LONG (_data_size) ; + LONG (_bss_size) ; + /* The linker will put a SHORT here with the version of + the IRX (or zero if there is no version). */ + /* The linker will put a null terminated string here + containing the name of the IRX (or an empty string if + the name is not known). */ + } + . = 0x0 ; + _ftext = . ; + .text : { + CREATE_OBJECT_SYMBOLS + * ( .text ) + * ( .text.* ) + * ( .init ) + * ( .fini ) + } = 0 + _etext = . ; + . = . ; + _fdata = . ; + .rodata : { + * ( .rdata ) + * ( .rodata ) + * ( .rodata1 ) + * ( .rodata.* ) + } = 0 + .data : { + * ( .data ) + * ( .data1 ) + * ( .data.* ) + CONSTRUCTORS + } + . = ALIGN(16) ; + _gp = . + 0x8000 ; + .sdata : { + * ( .lit8 ) + * ( .lit4 ) + * ( .sdata ) + * ( .sdata.* ) + } + _edata = . ; + . = ALIGN(4) ; + _fbss = . ; + .sbss : { + * ( .sbss ) + * ( .scommon ) + } + _bss_start = . ; + .bss : { + * ( .bss ) + * ( COMMON ) + . = ALIGN(4) ; + } + _end = . ; + _text_size = _etext - _ftext ; + _data_size = _edata - _fdata ; + _bss_size = _end - _fbss ; + /* This is the stuff that we don't want to be put in an IRX. */ + /DISCARD/ : { + * ( .reginfo ) + } +} diff --git a/pcsx2hostfs/iop/net_fio.c b/pcsx2hostfs/iop/net_fio.c new file mode 100644 index 0000000000..9b9cb9665d --- /dev/null +++ b/pcsx2hostfs/iop/net_fio.c @@ -0,0 +1,230 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +// Fu*k knows why net_fio & net_fsys are separated.. + +#include +#include +#include +#include +#include + +#include + +#include "net_fio.h" +#include "hostlink.h" + +#define PACKET_MAXSIZE 1024 +static int send_packet[PACKET_MAXSIZE] __attribute__((aligned(16))); + +#ifdef DEBUG +#define dbgprintf(args...) printf(args) +#else +#define dbgprintf(args...) do { } while(0) +#endif + +//---------------------------------------------------------------------- +// +#define PCSX2_FIO_BASE ((volatile unsigned int*)0x1d000800) +#define PCSX2_FIO_RDID (PCSX2_FIO_BASE + 0) // [f0] on read: returns the FIO magic number 'E2SP' +#define PCSX2_FIO_WCMD (PCSX2_FIO_BASE + 0) // [f0] on write: executes a command (usually "call function") +#define PCSX2_FIO_DLEN (PCSX2_FIO_BASE + 1) // [f4] write only: length of params data +#define PCSX2_FIO_DPTR (PCSX2_FIO_BASE + 2) // [f8] write only: mem address of params data +#define PCSX2_FIO_WFID (PCSX2_FIO_BASE + 3) // [fc] on write: function ID +#define PCSX2_FIO_RRET (PCSX2_FIO_BASE + 3) // [fc] on read: return value of the function + +#define PCSX2_FIO_CMD_CALL 1 + +int pcsx2fio_device_exists() +{ + return (*PCSX2_FIO_RDID == 'E2SP'); // PS2E +} + +int pcsx2fio_set_call(unsigned int func_id, void* params, int params_length) +{ + *PCSX2_FIO_DLEN = params_length; + *PCSX2_FIO_DPTR = (int)params; + *PCSX2_FIO_WFID = func_id; + *PCSX2_FIO_WCMD = PCSX2_FIO_CMD_CALL; + return *PCSX2_FIO_RRET; +} + +char *strcpy(char *dest, const char *src) +{ + while(*src) + { + *(dest++) = *(src++); + } + *(dest) = 0; + return dest; +} + +//---------------------------------------------------------------------- +// +void +pcsx2fio_close_fsys(void) +{ +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_open_file(char *path, int flags) +{ + send_packet[0] = flags; + strcpy((char*)(send_packet+1),path); + + int length = strlen(path) + 4; + + int ret = pcsx2fio_set_call(PS2E_FIO_OPEN_CMD, send_packet, length); + + return ret; +} + + +//---------------------------------------------------------------------- +// +int pcsx2fio_close_file(int fd) +{ + send_packet[0] = fd; + + int length = 4; + + int ret = pcsx2fio_set_call(PS2E_FIO_CLOSE_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_lseek_file(int fd, unsigned int offset, int whence) +{ + send_packet[0] = fd; + send_packet[1] = offset; + send_packet[2] = whence; + send_packet[3] = 0; + + int length = 12; + + int ret = pcsx2fio_set_call(PS2E_FIO_LSEEK_CMD, send_packet, length); + + return ret; +} + + +//---------------------------------------------------------------------- +// +int pcsx2fio_write_file(int fd, char *buf, int _length) +{ + send_packet[0] = fd; + send_packet[1] = (int)buf; + send_packet[2] = _length; + send_packet[3] = 0; + + int length = 12; + + int ret = pcsx2fio_set_call(PS2E_FIO_WRITE_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_read_file(int fd, char *buf, int _length) +{ + send_packet[0] = fd; + send_packet[1] = (int)buf; + send_packet[2] = _length; + send_packet[3] = 0; + + int length = 12; + + int ret = pcsx2fio_set_call(PS2E_FIO_READ_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_remove(char *name) +{ + strcpy((char*)(send_packet+0),name); + + int length = strlen(name); + + int ret = pcsx2fio_set_call(PS2E_FIO_REMOVE_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_mkdir(char *name, int mode) +{ + send_packet[0] = mode; + strcpy((char*)(send_packet+1),name); + + int length = strlen(name) + 4; + + int ret = pcsx2fio_set_call(PS2E_FIO_MKDIR_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_rmdir(char *name) +{ + strcpy((char*)(send_packet+0),name); + + int length = strlen(name); + + int ret = pcsx2fio_set_call(PS2E_FIO_RMDIR_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_open_dir(char *path) +{ + strcpy((char*)(send_packet+0),path); + + int length = strlen(path); + + int ret = pcsx2fio_set_call(PS2E_FIO_OPENDIR_CMD, send_packet, length); + + return ret; +} + +//---------------------------------------------------------------------- +// +int pcsx2fio_read_dir(int fd, void *buf) +{ + send_packet[0] = fd; + send_packet[1] = (int)buf; + + int length = 8; + + int ret = pcsx2fio_set_call(PS2E_FIO_READDIR_CMD, send_packet, length); + + return ret; +} + + +//---------------------------------------------------------------------- +// +int pcsx2fio_close_dir(int fd) +{ + send_packet[0] = fd; + + int length = 4; + + int ret = pcsx2fio_set_call(PS2E_FIO_CLOSEDIR_CMD, send_packet, length); + + return ret; +} diff --git a/pcsx2hostfs/iop/net_fio.h b/pcsx2hostfs/iop/net_fio.h new file mode 100644 index 0000000000..d136530ad9 --- /dev/null +++ b/pcsx2hostfs/iop/net_fio.h @@ -0,0 +1,33 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#ifndef _NETFIO_H_ +#define _NETFIO_H_ + +int pcsx2fio_device_exists(); + +int pcsx2fio_set_call(unsigned int func_id, void* params, int params_length); + +int pcsx2fio_file_serv(void *arg); +int pcsx2fio_recv_bytes(int fd, char *buf, int bytes); +int pcsx2fio_accept_pkt(int fd, char *buf, int len, int pkt_type); +int pcsx2fio_open_file(char *path, int flags); +int pcsx2fio_close_file(int fd); +int pcsx2fio_read_file(int fd, char *buf, int length); +int pcsx2fio_write_file(int fd, char *buf, int length); +int pcsx2fio_lseek_file(int fd, unsigned int offset, int whence); +void pcsx2fio_close_socket(void); +void pcsx2fio_close_fsys(void); +int pcsx2fio_remove(char *name); +int pcsx2fio_mkdir(char *name, int mode); +int pcsx2fio_rmdir(char *name); +int pcsx2fio_open_dir(char *path); +int pcsx2fio_read_dir(int fd, void *buf); +int pcsx2fio_close_dir(int fd); + +#endif diff --git a/pcsx2hostfs/iop/net_fsys.c b/pcsx2hostfs/iop/net_fsys.c new file mode 100644 index 0000000000..6941838ee7 --- /dev/null +++ b/pcsx2hostfs/iop/net_fsys.c @@ -0,0 +1,306 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * Copyright (C) 2004 adresd (adresd_ps2dev@yahoo.com) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "net_fio.h" + +#ifdef DEBUG +#define dbgprintf(args...) printf(args) +#else +#define dbgprintf(args...) do { } while(0) +#endif + +static char fsname[] = "host"; + +//////////////////////////////////////////////////////////////////////// +//static iop_device_t fsys_driver; + +/* File desc struct is probably larger than this, but we only + * need to access the word @ offset 0x0C (in which we put our identifier) + */ +struct filedesc_info +{ + int unkn0; + int unkn4; + int device_id; // the X in hostX + int own_fd; +}; + +//////////////////////////////////////////////////////////////////////// +/* We need(?) to protect the net access, so the server doesn't get + * confused if two processes calls a fsys func at the same time + */ +static int fsys_sema; +static int fsys_pid = 0; +//static iop_device_t fsys_driver; + +//////////////////////////////////////////////////////////////////////// +static int dummy5() +{ + printf("dummy function called\n"); + return -5; +} + +//////////////////////////////////////////////////////////////////////// +static void fsysInit(iop_device_t *driver) +{ + +} + +//////////////////////////////////////////////////////////////////////// +static int fsysDestroy(void) +{ + WaitSema(fsys_sema); + pcsx2fio_close_fsys(); + // ExitDeleteThread(fsys_pid); + SignalSema(fsys_sema); + DeleteSema(fsys_sema); + return 0; +} + + +//////////////////////////////////////////////////////////////////////// +static int fsysOpen( int fd, char *name, int mode) +{ + struct filedesc_info *fd_info; + int fsys_fd; + + dbgprintf("fsysOpen..\n"); + dbgprintf(" fd: %x, name: %s, mode: %d\n\n", fd, name, mode); + + fd_info = (struct filedesc_info *)fd; + + WaitSema(fsys_sema); + fsys_fd = pcsx2fio_open_file(name, mode); + SignalSema(fsys_sema); + fd_info->own_fd = fsys_fd; + + return fsys_fd; +} + + + +//////////////////////////////////////////////////////////////////////// +static int fsysClose( int fd) +{ + struct filedesc_info *fd_info; + int ret; + + dbgprintf("fsys_close..\n" + " fd: %x\n\n", fd); + + fd_info = (struct filedesc_info *)fd; + WaitSema(fsys_sema); + ret = pcsx2fio_close_file(fd_info->own_fd); + SignalSema(fsys_sema); + + return ret; +} + + + +//////////////////////////////////////////////////////////////////////// +static int fsysRead( int fd, char *buf, int size) +{ + struct filedesc_info *fd_info; + int ret; + + fd_info = (struct filedesc_info *)fd; + + dbgprintf("fsysRead..." + " fd: %x\n" + " bf: %x\n" + " sz: %d\n" + " ow: %d\n\n", fd, (int)buf, size, fd_info->own_fd); + + WaitSema(fsys_sema); + ret = pcsx2fio_read_file(fd_info->own_fd, buf, size); + SignalSema(fsys_sema); + + return ret; +} + + + + +//////////////////////////////////////////////////////////////////////// +static int fsysWrite( int fd, char *buf, int size) +{ + struct filedesc_info *fd_info; + int ret; + + dbgprintf("fsysWrite..." + " fd: %x\n", fd); + + fd_info = (struct filedesc_info *)fd; + WaitSema(fsys_sema); + ret = pcsx2fio_write_file(fd_info->own_fd, buf, size); + SignalSema(fsys_sema); + return ret; +} + + + +//////////////////////////////////////////////////////////////////////// +static int fsysLseek( int fd, unsigned int offset, int whence) +{ + struct filedesc_info *fd_info; + int ret; + + dbgprintf("fsysLseek..\n" + " fd: %x\n" + " of: %x\n" + " wh: %x\n\n", fd, offset, whence); + + fd_info = (struct filedesc_info *)fd; + WaitSema(fsys_sema); + ret = pcsx2fio_lseek_file(fd_info->own_fd, offset, whence); + SignalSema(fsys_sema); + return ret; +} + +//////////////////////////////////////////////////////////////////////// +static int fsysRemove(iop_file_t* file, char *name) +{ + int ret; + dbgprintf("fsysRemove..\n"); + dbgprintf(" name: %s\n\n", name); + + WaitSema(fsys_sema); + ret = pcsx2fio_remove(name); + SignalSema(fsys_sema); + + return ret; +} + +//////////////////////////////////////////////////////////////////////// +static int fsysMkdir(iop_file_t* file, char *name, int mode) +{ + int ret; + dbgprintf("fsysMkdir..\n"); + dbgprintf(" name: '%s'\n\n", name); + + WaitSema(fsys_sema); + ret = pcsx2fio_mkdir(name, mode); + SignalSema(fsys_sema); + + return ret; +} + +//////////////////////////////////////////////////////////////////////// +static int fsysRmdir(iop_file_t* file, char *name) +{ + int ret; + dbgprintf("fsysRmdir..\n"); + dbgprintf(" name: %s\n\n", name); + + WaitSema(fsys_sema); + ret = pcsx2fio_rmdir(name); + SignalSema(fsys_sema); + + return ret; +} + + +//////////////////////////////////////////////////////////////////////// +static int fsysDopen(int fd, char *name) +{ + struct filedesc_info *fd_info; + int fsys_fd; + + dbgprintf("fsysDopen..\n"); + dbgprintf(" fd: %x, name: %s\n\n", fd, name); + + fd_info = (struct filedesc_info *)fd; + + WaitSema(fsys_sema); + fsys_fd = pcsx2fio_open_dir(name); + SignalSema(fsys_sema); + fd_info->own_fd = fsys_fd; + + return fsys_fd; +} + +//////////////////////////////////////////////////////////////////////// +static int fsysDread(int fd, void *buf) +{ + struct filedesc_info *fd_info; + int ret; + + fd_info = (struct filedesc_info *)fd; + + dbgprintf("fsysDread..." + " fd: %x\n" + " bf: %x\n" + " ow: %d\n\n", fd, (int)buf, fd_info->own_fd); + + WaitSema(fsys_sema); + ret = pcsx2fio_read_dir(fd_info->own_fd, buf); + SignalSema(fsys_sema); + + return ret; + +} + +//////////////////////////////////////////////////////////////////////// +static int fsysDclose(int fd) +{ + struct filedesc_info *fd_info; + int ret; + + dbgprintf("fsys_dclose..\n" + " fd: %x\n\n", fd); + + fd_info = (struct filedesc_info *)fd; + WaitSema(fsys_sema); + ret = pcsx2fio_close_dir(fd_info->own_fd); + SignalSema(fsys_sema); + + return ret; +} + +iop_device_ops_t fsys_functarray = { (void *)fsysInit, (void *)fsysDestroy, (void *)dummy5, + (void *)fsysOpen, (void *)fsysClose, (void *)fsysRead, + (void *)fsysWrite, (void *)fsysLseek, (void *)dummy5, + (void *)fsysRemove, (void *)fsysMkdir, (void *)fsysRmdir, + (void *)fsysDopen, (void *)fsysDclose, (void *)fsysDread, + (void *)dummy5, (void *)dummy5 }; + +iop_device_t fsys_driver = { fsname, 16, 1, "fsys driver", + &fsys_functarray }; +//////////////////////////////////////////////////////////////////////// +// Entry point for mounting the file system +int fsysMount(void) +{ + iop_sema_t sema_info; + + sema_info.attr = 1; + sema_info.option = 0; + sema_info.initial = 1; + sema_info.max = 1; + fsys_sema = CreateSema(&sema_info); + + DelDrv(fsname); + AddDrv(&fsys_driver); + + return 0; +} + +int fsysUnmount(void) +{ + DelDrv(fsname); + return 0; +} diff --git a/pcsx2hostfs/iop/nprintf.c b/pcsx2hostfs/iop/nprintf.c new file mode 100644 index 0000000000..449c15e898 --- /dev/null +++ b/pcsx2hostfs/iop/nprintf.c @@ -0,0 +1,82 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////// +#define NPM_PUTS 0x01 +#define RPC_NPM_USER 0x014d704e + +//////////////////////////////////////////////////////////////////////// +static void * +naplinkRpcHandler(int cmd, void *buffer, int size) +{ + // Only supports npmPrintf of course + switch(cmd) { + case NPM_PUTS: + printf(buffer); + break; + default: + printf("unknown npm rpc call\n"); + } + + return buffer; +} + +//////////////////////////////////////////////////////////////////////// +static SifRpcServerData_t server __attribute((aligned(16))); +static SifRpcDataQueue_t queue __attribute((aligned(16))); +static unsigned char rpc_buffer[512] __attribute((aligned(16))); + +static void +napThread(void *arg) +{ + int pid; + + SifInitRpc(0); + pid = GetThreadId(); + SifSetRpcQueue(&queue, pid); + SifRegisterRpc(&server, RPC_NPM_USER, naplinkRpcHandler, + rpc_buffer, 0, 0, &queue); + SifRpcLoop(&queue); // Never exits + ExitDeleteThread(); +} +//////////////////////////////////////////////////////////////////////// +int +naplinkRpcInit(void) +{ + struct _iop_thread th_attr; + int ret; + int pid; + + th_attr.attr = 0x02000000; + th_attr.option = 0; + th_attr.thread = napThread; + th_attr.stacksize = 0x800; + th_attr.priority = 79; + + pid = CreateThread(&th_attr); + if (pid < 0) { + printf("IOP: napRpc createThread failed %d\n", pid); + return -1; + } + + ret = StartThread(pid, 0); + if (ret < 0) { + printf("IOP: napRpc startThread failed %d\n", ret); + DeleteThread(pid); + return -1; + } + return 0; +} diff --git a/pcsx2hostfs/iop/pcsx2hostfs.irx b/pcsx2hostfs/iop/pcsx2hostfs.irx new file mode 100644 index 0000000000000000000000000000000000000000..0ff777f2eb20764717690f17c41ea0cee147b0cb GIT binary patch literal 20305 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 n7vVlr_ +#include +#include +#include +#include +#include +#include +#include "excepHandler.h" + +// Entry points +extern int fsysMount(void); +extern int cmdHandlerInit(void); +extern int ttyMount(void); +extern int naplinkRpcInit(void); +//////////////////////////////////////////////////////////////////////// +// main +// start threads & init rpc & filesys +int +_start( int argc, char **argv) +{ + ttyWrite(NULL,"TEST",4); + + FlushDcache(); + CpuEnableIntr(0); + + SifInitRpc(0); + + pcsx2fio_device_exists(); + + if ((argc < 2) || (strncmp(argv[1], "-notty", 6))) { + ttyMount(); + // Oh well.. There's a bug in either smapif or lwip's etharp + // that thrashes udp msgs which are queued while waiting for arp + // request + // alas, this msg will probably not be displayed + printf("tty mounted\n"); + } + + fsysMount(); + printf("host: mounted\n"); + naplinkRpcInit(); + printf("Naplink thread started\n"); + + installExceptionHandlers(); + + return 0; +} + diff --git a/pcsx2hostfs/iop/tty.c b/pcsx2hostfs/iop/tty.c new file mode 100644 index 0000000000..a7364d84c2 --- /dev/null +++ b/pcsx2hostfs/iop/tty.c @@ -0,0 +1,105 @@ +/********************************************************************* + * Copyright (C) 2003 Tord Lindstrom (pukko@home.se) + * This file is subject to the terms and conditions of the PS2Link License. + * See the file LICENSE in the main directory of this distribution for more + * details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "net_fio.h" +#include "hostlink.h" + +//////////////////////////////////////////////////////////////////////// + +static int tty_socket = 0; +static int tty_sema = -1; + +static char ttyname[] = "tty"; + +//////////////////////////////////////////////////////////////////////// +static int dummy() +{ + return -5; +} + +//////////////////////////////////////////////////////////////////////// +static int dummy0() +{ + return 0; +} + +//////////////////////////////////////////////////////////////////////// +static int ttyInit(iop_device_t *driver) +{ + iop_sema_t sema_info; + + sema_info.attr = 0; + sema_info.initial = 1; /* Unlocked. */ + sema_info.max = 1; + if ((tty_sema = CreateSema(&sema_info)) < 0) + return -1; + + + if(!pcsx2fio_device_exists()) + return -1; + + return 1; +} + +//////////////////////////////////////////////////////////////////////// +static int ttyOpen( int fd, char *name, int mode) +{ + return 1; +} + +//////////////////////////////////////////////////////////////////////// +static int ttyClose( int fd) +{ + return 1; +} + + +//////////////////////////////////////////////////////////////////////// +int ttyWrite(iop_file_t *file, char *buf, int size) +{ + int res; + + WaitSema(tty_sema); + + res = pcsx2fio_set_call(PS2E_FIO_PRINTF_CMD,buf,size); + + SignalSema(tty_sema); + return res; +} + +iop_device_ops_t tty_functarray = { ttyInit, dummy0, (void *)dummy, + (void *)ttyOpen, (void *)ttyClose, (void *)dummy, + (void *)ttyWrite, (void *)dummy, (void *)dummy, + (void *)dummy, (void *)dummy, (void *)dummy, + (void *)dummy, (void *)dummy, (void *)dummy, + (void *)dummy, (void *)dummy }; + +iop_device_t tty_driver = { ttyname, 3, 1, "Fast TTY for pcsx2", + &tty_functarray }; + +//////////////////////////////////////////////////////////////////////// +// Entry point for mounting the file system +int ttyMount(void) +{ + close(0); + close(1); + DelDrv(ttyname); + AddDrv(&tty_driver); + if(open("tty00:", O_RDONLY) != 0) while(1); + if(open("tty00:", O_WRONLY) != 1) while(1); + + return 0; +} diff --git a/pcsx2hostfs/pcsx2hostfs.sln b/pcsx2hostfs/pcsx2hostfs.sln new file mode 100644 index 0000000000..075f4870ca --- /dev/null +++ b/pcsx2hostfs/pcsx2hostfs.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcsx2hostfs", "pcsx2hostfs.vcproj", "{9728FF5E-4EC6-4D47-995A-05629DCDE23F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9728FF5E-4EC6-4D47-995A-05629DCDE23F}.Debug|Win32.ActiveCfg = Debug|Win32 + {9728FF5E-4EC6-4D47-995A-05629DCDE23F}.Debug|Win32.Build.0 = Debug|Win32 + {9728FF5E-4EC6-4D47-995A-05629DCDE23F}.Release|Win32.ActiveCfg = Release|Win32 + {9728FF5E-4EC6-4D47-995A-05629DCDE23F}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/pcsx2hostfs/pcsx2hostfs.vcproj b/pcsx2hostfs/pcsx2hostfs.vcproj new file mode 100644 index 0000000000..aff9c57583 --- /dev/null +++ b/pcsx2hostfs/pcsx2hostfs.vcproj @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +