diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..139bcce --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2005/2006 James F +Copyright (c) 2005/2006 Julian T +Copyright (c) 2006 Rasmus B +Copyright (c) 2005 John Kelley +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fcde158 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +all: + $(MAKE) -f Makefile.psp all + $(MAKE) -f Makefile.oe all + $(MAKE) -f Makefile.tools all + +release: + $(MAKE) -f Makefile.psp release + $(MAKE) -f Makefile.oe release + $(MAKE) -f Makefile.tools release + +clean: + $(MAKE) -f Makefile.psp clean + $(MAKE) -f Makefile.oe clean + $(MAKE) -f Makefile.tools clean diff --git a/Makefile.clients b/Makefile.clients new file mode 100644 index 0000000..5fe7671 --- /dev/null +++ b/Makefile.clients @@ -0,0 +1,15 @@ + +all: + $(MAKE) -C pspsh all + $(MAKE) -C usbhostfs_pc all + if ( which sdl-config ); then { $(MAKE) -C tools/remotejoy/pcsdl all; } else { $(MAKE) -C tools/remotejoy/pc all; } fi + +install: all + $(MAKE) -C pspsh install + $(MAKE) -C usbhostfs_pc install + if ( which sdl-config ); then { $(MAKE) -C tools/remotejoy/pcsdl install; } else { $(MAKE) -C tools/remotejoy/pc install; } fi + +clean: + $(MAKE) -C pspsh clean + $(MAKE) -C usbhostfs_pc clean + if ( which sdl-config ); then { $(MAKE) -C tools/remotejoy/pcsdl clean; } else { $(MAKE) -C tools/remotejoy/pc clean; } fi diff --git a/Makefile.oe b/Makefile.oe new file mode 100644 index 0000000..9a2598f --- /dev/null +++ b/Makefile.oe @@ -0,0 +1,43 @@ +all: + $(MAKE) -C libpsplink all + $(MAKE) -C libpsplink_driver all + $(MAKE) -C libusbhostfs all + $(MAKE) -C libusbhostfs_driver all + $(MAKE) -C psplink all PSP_FW_VERSION=271 + $(MAKE) -C psplink_user all PSP_FW_VERSION=271 + $(MAKE) -C usbhostfs all PSP_FW_VERSION=271 + $(MAKE) -C usbgdb all PSP_FW_VERSION=271 + $(MAKE) -C boot271 release PSP_FW_VERSION=271 + +release: clean all + mkdir -p release_oe/ + mkdir -p release_oe/pc + mkdir -p release_oe/psplink + cp -Rf scripts release_oe/scripts + cp psplink/psplink.prx release_oe/psplink + cp psplink/psplink.ini release_oe/psplink + cp psplink_user/psplink_user.prx release_oe/psplink + cp usbhostfs/usbhostfs.prx release_oe/psplink + cp usbgdb/usbgdb.prx release_oe/psplink + cp boot271/EBOOT.PBP release_oe/psplink + cp -Rf pspsh release_oe/pc + mkdir release_oe/pc/psplink + cp psplink/shellcmd.h release_oe/pc/psplink + cp -Rf usbhostfs_pc release_oe/pc + cp -Rf windows release_oe/pc + cp usbhostfs/usbhostfs.h release_oe/pc/usbhostfs_pc + cp README release_oe + cp LICENSE release_oe + cp psplink_manual.pdf release_oe + +clean: + $(MAKE) -C libpsplink clean + $(MAKE) -C libpsplink_driver clean + $(MAKE) -C libusbhostfs clean + $(MAKE) -C libusbhostfs_driver clean + $(MAKE) -C psplink clean + $(MAKE) -C psplink_user clean + $(MAKE) -C usbhostfs clean + $(MAKE) -C usbgdb clean + $(MAKE) -C boot271 clean + rm -rf release_oe diff --git a/Makefile.psp b/Makefile.psp new file mode 100644 index 0000000..891a77c --- /dev/null +++ b/Makefile.psp @@ -0,0 +1,54 @@ +all: + $(MAKE) -C libpsplink all + $(MAKE) -C libpsplink_driver all + $(MAKE) -C libusbhostfs all + $(MAKE) -C libusbhostfs_driver all + $(MAKE) -C psplink all + $(MAKE) -C psplink_user all + $(MAKE) -C usbhostfs all + $(MAKE) -C usbgdb all + $(MAKE) -C bootstrap all + $(MAKE) -C bootstrap kxploit + +release: all + mkdir -p release/v1.0/psplink + mkdir -p release/v1.5 + mkdir -p release/v1.5_nocorrupt + mkdir -p release/pc + cp -Rf scripts release/scripts + cp bootstrap/EBOOT.PBP release/v1.0/psplink + cp psplink/psplink.prx release/v1.0/psplink + cp psplink/psplink.ini release/v1.0/psplink + cp psplink_user/psplink_user.prx release/v1.0/psplink + cp usbhostfs/usbhostfs.prx release/v1.0/psplink + cp usbgdb/usbgdb.prx release/v1.0/psplink + cp -R bootstrap/psplink release/v1.5 + cp -R bootstrap/psplink% release/v1.5 + cp psplink/psplink.prx release/v1.5/psplink + cp psplink/psplink.ini release/v1.5/psplink + cp psplink_user/psplink_user.prx release/v1.5/psplink + cp usbhostfs/usbhostfs.prx release/v1.5/psplink + cp usbgdb/usbgdb.prx release/v1.5/psplink + cp -R release/v1.5/psplink release/v1.5_nocorrupt/__SCE__psplink + cp -R release/v1.5/psplink% release/v1.5_nocorrupt/%__SCE__psplink + cp -Rf pspsh release/pc + mkdir release/pc/psplink + cp psplink/shellcmd.h release/pc/psplink + cp -Rf usbhostfs_pc release/pc + cp -Rf windows release/pc + cp usbhostfs/usbhostfs.h release/pc/usbhostfs_pc + cp README release + cp LICENSE release + cp psplink_manual.pdf release + +clean: + $(MAKE) -C libpsplink clean + $(MAKE) -C libpsplink_driver clean + $(MAKE) -C libusbhostfs clean + $(MAKE) -C libusbhostfs_driver clean + $(MAKE) -C psplink clean + $(MAKE) -C psplink_user clean + $(MAKE) -C usbhostfs clean + $(MAKE) -C usbgdb clean + $(MAKE) -C bootstrap clean + rm -rf release diff --git a/Makefile.tools b/Makefile.tools new file mode 100644 index 0000000..5a44e67 --- /dev/null +++ b/Makefile.tools @@ -0,0 +1,35 @@ +all: + $(MAKE) -C tools/debugmenu all + $(MAKE) -C tools/remotejoy all + $(MAKE) -C tools/scrkprintf all + $(MAKE) -C tools/siokprintf all + $(MAKE) -C tools/usbkprintf all + +release: all + cp tools/debugmenu/debugmenu.prx release/v1.0/psplink + cp tools/debugmenu/debugmenu.prx release/v1.5/psplink + cp tools/debugmenu/debugmenu.prx release/v1.5_nocorrupt/__SCE__psplink + cp tools/debugmenu/debugmenu.prx release_oe/psplink + cp tools/remotejoy/remotejoy.prx release/v1.0/psplink + cp tools/remotejoy/remotejoy.prx release/v1.5/psplink + cp tools/remotejoy/remotejoy.prx release/v1.5_nocorrupt/__SCE__psplink + cp tools/remotejoy/remotejoy.prx release_oe/psplink + cp tools/scrkprintf/scrkprintf.prx release/v1.0/psplink + cp tools/scrkprintf/scrkprintf.prx release/v1.5/psplink + cp tools/scrkprintf/scrkprintf.prx release/v1.5_nocorrupt/__SCE__psplink + cp tools/scrkprintf/scrkprintf.prx release_oe/psplink + cp tools/siokprintf/siokprintf.prx release/v1.0/psplink + cp tools/siokprintf/siokprintf.prx release/v1.5/psplink + cp tools/siokprintf/siokprintf.prx release/v1.5_nocorrupt/__SCE__psplink + cp tools/siokprintf/siokprintf.prx release_oe/psplink + cp tools/usbkprintf/usbkprintf.prx release/v1.0/psplink + cp tools/usbkprintf/usbkprintf.prx release/v1.5/psplink + cp tools/usbkprintf/usbkprintf.prx release/v1.5_nocorrupt/__SCE__psplink + cp tools/usbkprintf/usbkprintf.prx release_oe/psplink + +clean: + $(MAKE) -C tools/debugmenu clean + $(MAKE) -C tools/remotejoy clean + $(MAKE) -C tools/scrkprintf clean + $(MAKE) -C tools/siokprintf clean + $(MAKE) -C tools/usbkprintf clean diff --git a/README b/README new file mode 100644 index 0000000..7d5b794 --- /dev/null +++ b/README @@ -0,0 +1,10 @@ +PSPLINK 3.0 README +(c) TyRaNiD 2005/2006/2007 +(c) Julian T 2005/2006 +(c) Rasmus B 2006 +(c) John_K 2005 + +PSPLINK is licensed under the BSD license, see LICENSE in PSPLINK root for details. + +Please read the manual document for installation instructions and an example +of usage. diff --git a/boot271/EBOOT.PBP b/boot271/EBOOT.PBP new file mode 100644 index 0000000..bf23620 Binary files /dev/null and b/boot271/EBOOT.PBP differ diff --git a/boot271/Makefile b/boot271/Makefile new file mode 100644 index 0000000..40f849d --- /dev/null +++ b/boot271/Makefile @@ -0,0 +1,20 @@ +release: all + mksfoex -d MEMSIZE=1 'PSPLink v3.0 OE' PARAM.SFO + pack-pbp EBOOT.PBP PARAM.SFO psplink.png NULL NULL NULL NULL psplink_boot.prx NULL + +TARGET = psplink_boot +OBJS = main.o + +INCDIR = +CFLAGS = -O2 -G0 -Wall +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = +LDFLAGS = + +PSP_LARGE_MEMORY = 1 +BUILD_PRX = 1 + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak diff --git a/boot271/PARAM.SFO b/boot271/PARAM.SFO new file mode 100644 index 0000000..623da8b Binary files /dev/null and b/boot271/PARAM.SFO differ diff --git a/boot271/main.c b/boot271/main.c new file mode 100644 index 0000000..b904fce --- /dev/null +++ b/boot271/main.c @@ -0,0 +1,83 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK bootstrap for 271 + * + * Copyright (c) 2005 James F + * + * $HeadURL$ + * $Id$ + */ + +#include +#include +#include +#include "../psplink/version.h" + +/* Define the module info section, note the 0x1000 flag to enable start in kernel mode */ +PSP_MODULE_INFO("PSPLINKLOADER", 0, 1, 1); + +/* Define the thread attribute as 0 so that the main thread does not get converted to user mode */ +PSP_MAIN_THREAD_ATTR(0); + +#define MODULE "psplink.prx" + +int _main(SceSize args, void *argp) +{ + char path[1024]; + char *slash; + + do + { + pspDebugScreenInit(); + pspDebugScreenPrintf("PSPLink Bootstrap TyRaNiD (c) 2k7 Version %s\n", PSPLINK_VERSION); + strcpy(path, argp); + slash = strrchr(path, '/'); + if(slash == NULL) + { + pspDebugScreenPrintf("Could not find last slash\n"); + break; + } + slash++; + *slash = 0; + strcat(path, MODULE); + + SceUID mod = sceKernelLoadModule(path, 0, NULL); + if (mod < 0) + { + pspDebugScreenPrintf("Error 0x%08X loading module.\n", mod); + break; + } + + mod = sceKernelStartModule(mod, args, argp, NULL, NULL); + if (mod < 0) + { + pspDebugScreenPrintf("Error 0x%08X starting module.\n", mod); + break; + } + + sceKernelSelfStopUnloadModule(1, 0, NULL); + } + while(0); + + sceKernelDelayThread(2000000); + sceKernelExitGame(); + + return 0; +} + +int module_start(SceSize args, void *argp) +{ + SceUID uid; + + uid = sceKernelCreateThread("PsplinkBoot", _main, 32, 0x10000, 0, 0); + if(uid < 0) + { + return 1; + } + sceKernelStartThread(uid, args, argp); + + return 0; +} diff --git a/boot271/psplink.png b/boot271/psplink.png new file mode 100644 index 0000000..a9443cd Binary files /dev/null and b/boot271/psplink.png differ diff --git a/bootstrap/Makefile b/bootstrap/Makefile new file mode 100644 index 0000000..54a7cc8 --- /dev/null +++ b/bootstrap/Makefile @@ -0,0 +1,21 @@ +TARGET = psplink +OBJS = main.o exports.o + +USE_PSPSDK_LIBC = 1 + +INCDIR = +CFLAGS = -O2 -G0 -Wall +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = +LDFLAGS = -mno-crt0 -nostartfiles -Tbootlink + +LIBS = + +EXTRA_TARGETS = EBOOT.PBP +PSP_EBOOT_TITLE = PSPLink 3.0 +PSP_EBOOT_ICON = psplink.png + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build.mak diff --git a/bootstrap/bootlink b/bootstrap/bootlink new file mode 100644 index 0000000..effdcb4 --- /dev/null +++ b/bootstrap/bootlink @@ -0,0 +1,246 @@ +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("elf32-littlemips", "elf32-bigmips", + "elf32-littlemips") +OUTPUT_ARCH(mips:allegrex) +ENTRY(module_start) +SEARCH_DIR("/usr/local/pspdev/psp/lib"); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = 0x09cd3000); . = 0x09cd3000; + .interp : { *(.interp) } + .dynamic : { *(.dynamic) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } + .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + /* PSP-specific relocations. */ + .rel.sceStub.text : { *(.rel.sceStub.text) *(SORT(.rel.sceStub.text.*)) } + .rel.lib.ent.top : { *(.rel.lib.ent.top) } + .rel.lib.ent : { *(.rel.lib.ent) } + .rel.lib.ent.btm : { *(.rel.lib.ent.btm) } + .rel.lib.stub.top : { *(.rel.lib.stub.top) } + .rel.lib.stub : { *(.rel.lib.stub) } + .rel.lib.stub.btm : { *(.rel.lib.stub.btm) } + .rel.rodata.sceModuleInfo : { *(.rel.rodata.sceModuleInfo) } + .rel.rodata.sceResident : { *(.rel.rodata.sceResident) } + .rel.rodata.sceNid : { *(.rel.rodata.sceNid) } + .rel.rodata.sceVstub : { *(.rel.rodata.sceVstub) *(SORT(.rel.rodata.sceVstub.*)) } + .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } + .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } + .rel.data.rel.ro : { *(.rel.data.rel.ro*) } + .rela.data.rel.ro : { *(.rel.data.rel.ro*) } + .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } + .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } + .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } + .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } + .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } + .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) } + .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) } + .rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) } + .rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) } + .rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) } + .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) } + .rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) } + .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) } + .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } + .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : + { + KEEP (*(.init)) + } =0 + .plt : { *(.plt) } + .text : + { + _ftext = . ; + *(.text .stub .text.* .gnu.linkonce.t.*) + KEEP (*(.text.*personality*)) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.mips16.fn.*) *(.mips16.call.*) + } =0 + .fini : + { + KEEP (*(.fini)) + } =0 + /* PSP library stub functions. */ + .sceStub.text : { *(.sceStub.text) *(SORT(.sceStub.text.*)) } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + /* PSP library entry table and library stub table. */ + .lib.ent.top : { *(.lib.ent.top) } + .lib.ent : { *(.lib.ent) } + .lib.ent.btm : { *(.lib.ent.btm) } + .lib.stub.top : { *(.lib.stub.top) } + .lib.stub : { *(.lib.stub) } + .lib.stub.btm : { *(.lib.stub.btm) } + /* PSP read-only data for module info, NIDs, and Vstubs. The + .rodata.sceModuleInfo section must appear before the .rodata section + otherwise it would get absorbed into .rodata and the PSP bootloader + would be unable to locate the module info structure. */ + .rodata.sceModuleInfo : { *(.rodata.sceModuleInfo) } + .rodata.sceResident : { *(.rodata.sceResident) } + .rodata.sceNid : { *(.rodata.sceNid) } + .rodata.sceVstub : { *(.rodata.sceVstub) *(SORT(.rodata.sceVstub.*)) } + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) } + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } + .eh_frame_hdr : { *(.eh_frame_hdr) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } + .gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(256) + (. & (256 - 1)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } + .gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) } + /* Thread Local Storage sections */ + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { KEEP (*(.preinit_array)) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { KEEP (*(.init_array)) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { KEEP (*(.fini_array)) } + PROVIDE (__fini_array_end = .); + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin*.o(.ctors)) + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin*.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) } + .data : + { + _fdata = . ; + *(.data .data.* .gnu.linkonce.d.*) + KEEP (*(.gnu.linkonce.d.*personality*)) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + . = .; + _gp = ALIGN(16) + 0x7ff0; + .got : { *(.got.plt) *(.got) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : + { + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + _fbss = .; + .sbss : + { + PROVIDE (__sbss_start = .); + PROVIDE (___sbss_start = .); + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + PROVIDE (__sbss_end = .); + PROVIDE (___sbss_end = .); + } + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); + } + . = ALIGN(32 / 8); + _end = .; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /DISCARD/ : { *(.comment) *(.pdr) } + /DISCARD/ : { *(.note.GNU-stack) } +} diff --git a/bootstrap/exports.exp b/bootstrap/exports.exp new file mode 100644 index 0000000..854c602 --- /dev/null +++ b/bootstrap/exports.exp @@ -0,0 +1,12 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/bootstrap/main.c b/bootstrap/main.c new file mode 100644 index 0000000..cab76cc --- /dev/null +++ b/bootstrap/main.c @@ -0,0 +1,126 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK bootstrap + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/bootstrap/main.c $ + * $Id: main.c 2200 2007-03-08 21:21:20Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include "../psplink/version.h" + +PSP_MODULE_INFO("PSPLINKLOADER", 0x1000, 1, 1); +/* Define the main thread's attribute value (optional) */ +PSP_MAIN_THREAD_ATTR(0); + +/* Define printf, just to make typing easier */ +#define printf pspDebugScreenPrintf + +SceUID load_module(const char *path, int flags, int type) +{ + SceKernelLMOption option; + SceUID mpid; + + /* If the type is 0, then load the module in the kernel partition, otherwise load it + in the user partition. */ + if (type == 0) { + mpid = 1; + } else { + mpid = 2; + } + + memset(&option, 0, sizeof(option)); + option.size = sizeof(option); + option.mpidtext = mpid; + option.mpiddata = mpid; + option.position = 0; + option.access = 1; + + return sceKernelLoadModule(path, flags, type > 0 ? &option : NULL); +} + +int main_thread(SceSize args, void *argp) +{ + char *argv0; + char prx_path[256]; + char *path; + SceUID modid; + int ret; + + pspDebugScreenInit(); + sceDisplayWaitVblankStart(); + + pspSdkInstallNoDeviceCheckPatch(); + pspSdkInstallNoPlainModuleCheckPatch(); + pspSdkInstallKernelLoadModulePatch(); + + argv0 = (char*) argp; + path = strrchr(argv0, '/'); + if(path != NULL) + { + memcpy(prx_path, argv0, path - argv0 + 1); + prx_path[path - argv0 + 1] = 0; + strcat(prx_path, "psplink.prx"); + } + else + { + /* Well try for a default */ + strcpy(prx_path, "ms0:/psplink.prx"); + } + + /* Start mymodule.prx and dump its information */ + printf("PSPLink Bootstrap TyRaNiD (c) 2k5 Version %s\n", PSPLINK_VERSION); + modid = load_module(prx_path, 0, 0); + if(modid >= 0) + { + int status; + + printf("Starting psplink module\n"); + ret = sceKernelStartModule(modid, args, argp, &status, NULL); + printf("Done\n"); + } + else + { + printf("Error loading psplink module %08X\n", modid); + } + + /* Let's bug out */ + sceKernelExitDeleteThread(0); + + return 0; +} + +int module_start(SceSize args, void *argp) __attribute__((alias("_start"))); + +/* Entry point */ +int _start(SceSize args, void *argp) +{ + int thid; + u32 func; + + func = (u32) main_thread; + func |= 0x80000000; + + /* Create a high priority thread */ + thid = sceKernelCreateThread("main_thread", (void *) func, 0x20, 0x10000, 0, NULL); + if(thid >= 0) + { + sceKernelStartThread(thid, args, argp); + } + + return 0; +} + +int module_stop(void) +{ + return 0; +} diff --git a/bootstrap/psplink%/EBOOT.PBP b/bootstrap/psplink%/EBOOT.PBP new file mode 100644 index 0000000..4abc377 Binary files /dev/null and b/bootstrap/psplink%/EBOOT.PBP differ diff --git a/bootstrap/psplink.png b/bootstrap/psplink.png new file mode 100644 index 0000000..a9443cd Binary files /dev/null and b/bootstrap/psplink.png differ diff --git a/bootstrap/psplink.xcf b/bootstrap/psplink.xcf new file mode 100644 index 0000000..6ef13fd Binary files /dev/null and b/bootstrap/psplink.xcf differ diff --git a/bootstrap/psplink/EBOOT.PBP b/bootstrap/psplink/EBOOT.PBP new file mode 100755 index 0000000..c14565f Binary files /dev/null and b/bootstrap/psplink/EBOOT.PBP differ diff --git a/libpsplink/Makefile b/libpsplink/Makefile new file mode 100644 index 0000000..3ebec36 --- /dev/null +++ b/libpsplink/Makefile @@ -0,0 +1,26 @@ +TARGET=libpsplink.a +all: $(TARGET) +OBJS = psplink_0000.o psplink_0001.o psplink_0002.o psplink_0003.o psplink_0004.o psplink_0005.o psplink_0006.o psplink_0007.o psplink_0008.o psplink_0009.o psplink_0010.o + +PSPSDK=$(shell psp-config --pspsdk-path) + +clean: + rm -f $(OBJS) $(TARGET) + +CC=psp-gcc +INCDIR = +CFLAGS = -Os -G0 -Wall -fno-builtin-printf -I$(PSPSDK)/include +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) +LDFLAGS=-nodefaultlibs + +LIBDIR = + +$(TARGET): $(OBJS) + psp-ar q $(TARGET) $(OBJS) + psp-ranlib $(TARGET) + + +$(OBJS): psplink.S + $(CC) $(CFLAGS) -DF_$* $< -c -o $@ + diff --git a/libpsplink/psplink.S b/libpsplink/psplink.S new file mode 100644 index 0000000..b5313ee --- /dev/null +++ b/libpsplink/psplink.S @@ -0,0 +1,40 @@ + .set noreorder + +#include "pspimport.s" + +// Build files +// psplink_0000.o psplink_0001.o psplink_0002.o psplink_0003.o psplink_0004.o psplink_0005.o psplink_0006.o psplink_0007.o psplink_0008.o psplink_0009.o psplink_0010.o + +#ifdef F_psplink_0000 + IMPORT_START "psplink",0x40090000 +#endif +#ifdef F_psplink_0001 + IMPORT_FUNC "psplink",0x670C6041,psplinkPresent +#endif +#ifdef F_psplink_0002 + IMPORT_FUNC "psplink",0xB03642D9,psplinkExitShell +#endif +#ifdef F_psplink_0003 + IMPORT_FUNC "psplink",0x8F31ACE7,psplinkReferModule +#endif +#ifdef F_psplink_0004 + IMPORT_FUNC "psplink",0x754F9BB8,psplinkReferModuleByName +#endif +#ifdef F_psplink_0005 + IMPORT_FUNC "psplink",0x4A45386D,psplinkReset +#endif +#ifdef F_psplink_0006 + IMPORT_FUNC "psplink",0xCBA8647D,apiHookByName +#endif +#ifdef F_psplink_0007 + IMPORT_FUNC "psplink",0x4ED59445,apiHookByNid +#endif +#ifdef F_psplink_0008 + IMPORT_FUNC "psplink",0x3E371C7A,_apiHookHandle +#endif +#ifdef F_psplink_0009 + IMPORT_FUNC "psplink",0x4285B6A4,_apiHookReturn +#endif +#ifdef F_psplink_0010 + IMPORT_FUNC "psplink",0x0A72C44E,apiHookRegisterUserDispatch +#endif diff --git a/libpsplink_driver/Makefile b/libpsplink_driver/Makefile new file mode 100644 index 0000000..323f183 --- /dev/null +++ b/libpsplink_driver/Makefile @@ -0,0 +1,26 @@ +TARGET=libpsplink_driver.a +all: $(TARGET) +OBJS = psplink_driver_0000.o psplink_driver_0001.o psplink_driver_0002.o psplink_driver_0003.o psplink_driver_0004.o psplink_driver_0005.o psplink_driver_0006.o psplink_driver_0007.o psplink_driver_0008.o psplink_driver_0009.o psplink_driver_0010.o psplink_driver_0011.o psplink_driver_0012.o psplink_driver_0013.o psplink_driver_0014.o psplink_driver_0015.o psplink_driver_0016.o psplink_driver_0017.o psplink_driver_0018.o + +PSPSDK=$(shell psp-config --pspsdk-path) + +clean: + rm -f $(OBJS) $(TARGET) + +CC=psp-gcc +INCDIR = +CFLAGS = -Os -G0 -Wall -fno-builtin-printf -I$(PSPSDK)/include +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) +LDFLAGS=-nodefaultlibs + +LIBDIR = + +$(TARGET): $(OBJS) + psp-ar q $(TARGET) $(OBJS) + psp-ranlib $(TARGET) + + +$(OBJS): psplink_driver.S + $(CC) $(CFLAGS) -DF_$* $< -c -o $@ + diff --git a/libpsplink_driver/psplink_driver.S b/libpsplink_driver/psplink_driver.S new file mode 100644 index 0000000..e209e22 --- /dev/null +++ b/libpsplink_driver/psplink_driver.S @@ -0,0 +1,61 @@ + .set noreorder + +#include "pspimport.s" + +#ifdef F_psplink_driver_0000 + IMPORT_START "psplink_driver",0x00090000 +#endif +#ifdef F_psplink_driver_0001 + IMPORT_FUNC "psplink_driver",0x670C6041,psplinkPresent +#endif +#ifdef F_psplink_driver_0002 + IMPORT_FUNC "psplink_driver",0xB03642D9,psplinkExitShell +#endif +#ifdef F_psplink_driver_0003 + IMPORT_FUNC "psplink_driver",0x8F31ACE7,psplinkReferModule +#endif +#ifdef F_psplink_driver_0004 + IMPORT_FUNC "psplink_driver",0x0D4438D3,psplinkGetFullThreadContext +#endif +#ifdef F_psplink_driver_0005 + IMPORT_FUNC "psplink_driver",0x754F9BB8,psplinkReferModuleByName +#endif +#ifdef F_psplink_driver_0006 + IMPORT_FUNC "psplink_driver",0x2FF7228E,psplinkReferThreadsByModule +#endif +#ifdef F_psplink_driver_0007 + IMPORT_FUNC "psplink_driver",0x4A45386D,psplinkReset +#endif +#ifdef F_psplink_driver_0008 + IMPORT_FUNC "psplink_driver",0xCBA8647D,apiHookByName +#endif +#ifdef F_psplink_driver_0009 + IMPORT_FUNC "psplink_driver",0x4ED59445,apiHookByNid +#endif +#ifdef F_psplink_driver_0010 + IMPORT_FUNC "psplink_driver",0xA79585AA,debugRegisterEventHandler +#endif +#ifdef F_psplink_driver_0011 + IMPORT_FUNC "psplink_driver",0x4FD9B166,debugUnregisterEventHandler +#endif +#ifdef F_psplink_driver_0012 + IMPORT_FUNC "psplink_driver",0xDA0CE45E,debugWaitDebugEvent +#endif +#ifdef F_psplink_driver_0013 + IMPORT_FUNC "psplink_driver",0x0B7159E4,debugDeleteBP +#endif +#ifdef F_psplink_driver_0014 + IMPORT_FUNC "psplink_driver",0x90440E35,debugDisableBP +#endif +#ifdef F_psplink_driver_0015 + IMPORT_FUNC "psplink_driver",0x4821391E,debugEnableBP +#endif +#ifdef F_psplink_driver_0016 + IMPORT_FUNC "psplink_driver",0x8BE7EA9B,debugFindBPByIndex +#endif +#ifdef F_psplink_driver_0017 + IMPORT_FUNC "psplink_driver",0xFCF4D9D3,debugSetBP +#endif +#ifdef F_psplink_driver_0018 + IMPORT_FUNC "psplink_driver",0x45EAC8C0,debugBreakThread +#endif diff --git a/libusbhostfs/Makefile b/libusbhostfs/Makefile new file mode 100644 index 0000000..3bfe7c4 --- /dev/null +++ b/libusbhostfs/Makefile @@ -0,0 +1,26 @@ +TARGET=libusbhostfs.a +all: $(TARGET) +OBJS = USBHostFS_0000.o USBHostFS_0001.o USBHostFS_0002.o USBHostFS_0003.o USBHostFS_0004.o USBHostFS_0005.o USBHostFS_0006.o USBHostFS_0007.o USBHostFS_0008.o USBHostFS_0009.o USBHostFS_0010.o + +PSPSDK=$(shell psp-config --pspsdk-path) + +clean: + rm -f $(OBJS) $(TARGET) + +CC=psp-gcc +INCDIR = +CFLAGS = -Os -G0 -Wall -fno-builtin-printf -I$(PSPSDK)/include +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) +LDFLAGS=-nodefaultlibs + +LIBDIR = + +$(TARGET): $(OBJS) + psp-ar q $(TARGET) $(OBJS) + psp-ranlib $(TARGET) + + +$(OBJS): USBHostFS.S + $(CC) $(CFLAGS) -DF_$* $< -c -o $@ + diff --git a/libusbhostfs/USBHostFS.S b/libusbhostfs/USBHostFS.S new file mode 100644 index 0000000..07a3902 --- /dev/null +++ b/libusbhostfs/USBHostFS.S @@ -0,0 +1,37 @@ + .set noreorder + +#include "pspimport.s" + +#ifdef F_USBHostFS_0000 + IMPORT_START "USBHostFS",0x40090000 +#endif +#ifdef F_USBHostFS_0001 + IMPORT_FUNC "USBHostFS",0x75246D41,usbAsyncRegister +#endif +#ifdef F_USBHostFS_0002 + IMPORT_FUNC "USBHostFS",0x587DDEDA,usbAsyncUnregister +#endif +#ifdef F_USBHostFS_0003 + IMPORT_FUNC "USBHostFS",0xBA9F1145,usbAsyncRead +#endif +#ifdef F_USBHostFS_0004 + IMPORT_FUNC "USBHostFS",0xE4C00162,usbAsyncReadWithTimeout +#endif +#ifdef F_USBHostFS_0005 + IMPORT_FUNC "USBHostFS",0x5D1F19A0,usbAsyncWrite +#endif +#ifdef F_USBHostFS_0006 + IMPORT_FUNC "USBHostFS",0x89D91015,usbAsyncFlush +#endif +#ifdef F_USBHostFS_0007 + IMPORT_FUNC "USBHostFS",0xFF18E862,usbWaitForConnect +#endif +#ifdef F_USBHostFS_0008 + IMPORT_FUNC "USBHostFS",0x4ABA9C2B,usbWriteBulkData +#endif +#ifdef F_USBHostFS_0009 + IMPORT_FUNC "USBHostFS",0x642EF71E,usbLockBus +#endif +#ifdef F_USBHostFS_0010 + IMPORT_FUNC "USBHostFS",0x439E6C6C,usbUnlockBus +#endif diff --git a/libusbhostfs_driver/Makefile b/libusbhostfs_driver/Makefile new file mode 100644 index 0000000..4eadafd --- /dev/null +++ b/libusbhostfs_driver/Makefile @@ -0,0 +1,26 @@ +TARGET=libusbhostfs_driver.a +all: $(TARGET) +OBJS = USBHostFS_driver_0000.o USBHostFS_driver_0001.o USBHostFS_driver_0002.o USBHostFS_driver_0003.o USBHostFS_driver_0004.o USBHostFS_driver_0005.o USBHostFS_driver_0006.o USBHostFS_driver_0007.o USBHostFS_driver_0008.o USBHostFS_driver_0009.o USBHostFS_driver_0010.o + +PSPSDK=$(shell psp-config --pspsdk-path) + +clean: + rm -f $(OBJS) $(TARGET) + +CC=psp-gcc +INCDIR = +CFLAGS = -Os -G0 -Wall -fno-builtin-printf -I$(PSPSDK)/include +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) +LDFLAGS=-nodefaultlibs + +LIBDIR = + +$(TARGET): $(OBJS) + psp-ar q $(TARGET) $(OBJS) + psp-ranlib $(TARGET) + + +$(OBJS): USBHostFS_driver.S + $(CC) $(CFLAGS) -DF_$* $< -c -o $@ + diff --git a/libusbhostfs_driver/USBHostFS_driver.S b/libusbhostfs_driver/USBHostFS_driver.S new file mode 100644 index 0000000..de2c9b9 --- /dev/null +++ b/libusbhostfs_driver/USBHostFS_driver.S @@ -0,0 +1,37 @@ + .set noreorder + +#include "pspimport.s" + +#ifdef F_USBHostFS_driver_0000 + IMPORT_START "USBHostFS_driver",0x00090000 +#endif +#ifdef F_USBHostFS_driver_0001 + IMPORT_FUNC "USBHostFS_driver",0x75246D41,usbAsyncRegister +#endif +#ifdef F_USBHostFS_driver_0002 + IMPORT_FUNC "USBHostFS_driver",0x587DDEDA,usbAsyncUnregister +#endif +#ifdef F_USBHostFS_driver_0003 + IMPORT_FUNC "USBHostFS_driver",0xBA9F1145,usbAsyncRead +#endif +#ifdef F_USBHostFS_driver_0004 + IMPORT_FUNC "USBHostFS_driver",0xE4C00162,usbAsyncReadWithTimeout +#endif +#ifdef F_USBHostFS_driver_0005 + IMPORT_FUNC "USBHostFS_driver",0x5D1F19A0,usbAsyncWrite +#endif +#ifdef F_USBHostFS_driver_0006 + IMPORT_FUNC "USBHostFS_driver",0x89D91015,usbAsyncFlush +#endif +#ifdef F_USBHostFS_driver_0007 + IMPORT_FUNC "USBHostFS_driver",0xFF18E862,usbWaitForConnect +#endif +#ifdef F_USBHostFS_driver_0008 + IMPORT_FUNC "USBHostFS_driver",0x4ABA9C2B,usbWriteBulkData +#endif +#ifdef F_USBHostFS_driver_0009 + IMPORT_FUNC "USBHostFS_driver",0x642EF71E,usbLockBus +#endif +#ifdef F_USBHostFS_driver_0010 + IMPORT_FUNC "USBHostFS_driver",0x439E6C6C,usbUnlockBus +#endif diff --git a/psplink/Makefile b/psplink/Makefile new file mode 100644 index 0000000..f181b82 --- /dev/null +++ b/psplink/Makefile @@ -0,0 +1,23 @@ +TARGET = psplink +OBJS = main.o shell.o config.o bitmap.o tty.o decodeaddr.o memoryUID.o kmode.o exception.o exception_asm.o psplinkcnf.o util.o debug.o libs.o apihook.o thctx.o stdio.o usbshell.o modload.o exports.o + +# Use the kernel's small inbuilt libc +USE_KERNEL_LIBC = 1 +#USE_PSPSDK_LIBC = 1 +# Use only kernel libraries +USE_KERNEL_LIBS = 1 + +INCDIR = ../usbhostfs +CFLAGS = -Os -G0 -Wall -fno-builtin-printf -ggdb +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) +LDFLAGS=-nodefaultlibs -L../libusbhostfs_driver + +LIBDIR = + +LIBS = -lpspusb_driver -lpspusbstor -lpspumd -lusbhostfs_driver + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak + +LIBS += -lpsppower_driver -lpspdisplay_driver diff --git a/psplink/apihook.c b/psplink/apihook.c new file mode 100644 index 0000000..ca70f84 --- /dev/null +++ b/psplink/apihook.c @@ -0,0 +1,530 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * apihook.c - User mode API hook code for psplink. + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/apihook.c $ + * $Id: apihook.c 2301 2007-08-26 13:48:05Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "psplink.h" +#include "libs.h" +#include "apihook.h" +#include "decodeaddr.h" + +#define APIHOOK_MAXNAME 32 +#define APIHOOK_MAXPARAM 6 +#define APIHOOK_MAXIDS 16 + +/* Define the api hooks */ +void _apiHook0(void); + +struct SyscallHeader +{ + void *unk; + unsigned int basenum; + unsigned int topnum; + unsigned int size; +}; + +#define PARAM_TYPE_INT 'i' +#define PARAM_TYPE_HEX 'x' +#define PARAM_TYPE_OCT 'o' +#define PARAM_TYPE_STR 's' +#define PARAM_TYPE_PTR 'p' + +#define RET_TYPE_VOID 'v' +#define RET_TYPE_HEX32 'x' +#define RET_TYPE_HEX64 'X' +#define RET_TYPE_INT32 'i' + +struct ApiHookGeneric +{ + /* Function name */ + char name[APIHOOK_MAXNAME]; + /* Parameter list */ + char param[APIHOOK_MAXPARAM]; + /* Return code */ + char ret; + /* Pointer to the real function, if NULL then invalid */ + void *func; + /* Pointer to the location in the syscall table */ + unsigned int *syscall; + /* Indicates if we should sleep the thread on the syscall */ + int sleep; + /* Indicates if this is an import hook or not */ + int imphook; +}; + +static unsigned int *g_userdispatch = NULL; + +static struct ApiHookGeneric g_apihooks[APIHOOK_MAXIDS] = { + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, + { "", "", 'v', NULL, NULL, 0, 0 }, +}; + +void *find_syscall_addr(unsigned int addr) +{ + struct SyscallHeader *head; + unsigned int *syscalls; + void **ptr; + int size; + int i; + + asm( + "cfc0 %0, $12\n" + : "=r"(ptr) + ); + + if(!ptr) + { + return NULL; + } + + head = (struct SyscallHeader *) *ptr; + syscalls = (unsigned int*) (*ptr + 0x10); + size = (head->size - 0x10) / sizeof(unsigned int); + + for(i = 0; i < size; i++) + { + if(syscalls[i] == addr) + { + return &syscalls[i]; + } + } + + return NULL; +} + +void apiHookRegisterUserDispatch(unsigned int *dispatch) +{ + g_userdispatch = dispatch; +} + +void *_apiHookHandle(int id, unsigned int *args) +{ + int intc; + void *func = NULL; + int k1; + + intc = pspSdkDisableInterrupts(); + if((id >= 0) && (id < APIHOOK_MAXIDS)) + { + func = g_apihooks[id].func; + } + pspSdkEnableInterrupts(intc); + + k1 = psplinkSetK1(0); + + if(func) + { + int i; + int j; + char str[128]; + int strleft; + + SHELL_PRINT("Function %s called from thread 0x%08X (RA:0x%08X)\n", g_apihooks[id].name, sceKernelGetThreadId(), sceKernelGetSyscallRA()); + for(i = 0; i < APIHOOK_MAXPARAM; i++) + { + if(g_apihooks[id].param[i]) + { + SHELL_PRINT("Arg %d: ", i); + switch(g_apihooks[id].param[i]) + { + case PARAM_TYPE_INT: SHELL_PRINT("%d\n", args[i]); + break; + case PARAM_TYPE_HEX: SHELL_PRINT("0x%08X\n", args[i]); + break; + case PARAM_TYPE_OCT: SHELL_PRINT("0%o\n", args[i]); + break; + case PARAM_TYPE_STR: strleft = memValidate(args[i], MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + if(strleft == 0) + { + SHELL_PRINT("Invalid pointer 0x%08X\n", args[i]); + break; + } + + if(strleft > (sizeof(str)-1)) + { + strleft = sizeof(str) - 1; + } + + strncpy(str, (const char *) args[i], strleft); + str[strleft] = 0; + + SHELL_PRINT("\"%s\"\n", str); + break; + case PARAM_TYPE_PTR: strleft = memValidate(args[i], MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + if(strleft == 0) + { + SHELL_PRINT("Invalid pointer 0x%08X\n", args[i]); + break; + } + + strleft = strleft > 32 ? 32 : strleft; + + SHELL_PRINT("0x%08X - ", args[i]); + for(j = 0; j < strleft; j++) + { + SHELL_PRINT("0x%02X ", _lb(args[i]+j)); + } + SHELL_PRINT("\n"); + break; + default: SHELL_PRINT("Unknown parameter type '%c'\n", g_apihooks[id].param[i]); + break; + }; + } + else + { + break; + } + } + + if(g_apihooks[id].sleep) + { + SHELL_PRINT("Sleeping thread 0x%08X\n", sceKernelGetThreadId()); + sceKernelSleepThread(); + } + } + + psplinkSetK1(k1); + + return func; +} + +void _apiHookReturn(int id, unsigned int* ret) +{ + int intc; + void *func = NULL; + int k1; + + intc = pspSdkDisableInterrupts(); + if((id >= 0) && (id < APIHOOK_MAXIDS)) + { + func = g_apihooks[id].func; + } + pspSdkEnableInterrupts(intc); + + k1 = psplinkSetK1(0); + + if(func) + { + SHELL_PRINT("Function %s returned ", g_apihooks[id].name); + switch(g_apihooks[id].ret) + { + case RET_TYPE_INT32: SHELL_PRINT("%d\n", ret[0]); + break; + case RET_TYPE_HEX32: SHELL_PRINT("0x%08X\n", ret[0]); + break; + case RET_TYPE_HEX64: SHELL_PRINT("0x%08X%08X\n", ret[1], ret[0]); + break; + default: SHELL_PRINT("void\n"); + break; + } + + if(g_apihooks[id].sleep) + { + SHELL_PRINT("Sleeping thread 0x%08X\n", sceKernelGetThreadId()); + sceKernelSleepThread(); + } + } + + psplinkSetK1(k1); +} + +void apiHookGenericPrint(void) +{ + int i; + + for(i = 0; i < APIHOOK_MAXIDS; i++) + { + if(g_apihooks[i].func) + { + SHELL_PRINT("Hook %2d: Name %s, Ret %c Param %.*s, Sleep %d, Syscall 0x%p Imp %d\n", i, + g_apihooks[i].name, g_apihooks[i].ret, APIHOOK_MAXPARAM, g_apihooks[i].param, + g_apihooks[i].sleep, g_apihooks[i].syscall, g_apihooks[i].imphook); + } + } +} + +static int find_free_hook(void) +{ + int i; + + for(i = 0; i < APIHOOK_MAXIDS; i++) + { + if(!g_apihooks[i].func) + { + break; + } + } + if(i < APIHOOK_MAXIDS) + { + return i; + } + + return -1; +} + +static void *apiHookAddr(unsigned int *addr, void *func) +{ + int intc; + + if(!addr) + { + return NULL; + } + + intc = pspSdkDisableInterrupts(); + *addr = (unsigned int) func; + sceKernelDcacheWritebackInvalidateRange(addr, sizeof(addr)); + sceKernelIcacheInvalidateRange(addr, sizeof(addr)); + pspSdkEnableInterrupts(intc); + + return addr; +} + +static void* apiHookImport(unsigned int *addr, void *func) +{ + int intc; + + if(!addr) + { + return NULL; + } + + intc = pspSdkDisableInterrupts(); + *addr = 0x08000000 | ((unsigned int) func & 0x0FFFFFFF) >> 2; + sceKernelDcacheWritebackInvalidateRange(addr, sizeof(addr)); + sceKernelIcacheInvalidateRange(addr, sizeof(addr)); + pspSdkEnableInterrupts(intc); + + return addr; +} + +void apiHookGenericDelete(int id) +{ + int intc; + + if((id < 0) || (id >= APIHOOK_MAXIDS)) + { + return; + } + + intc = pspSdkDisableInterrupts(); + /* Restore original function */ + if(g_apihooks[id].func) + { + if(g_apihooks[id].imphook) + { + apiHookImport(g_apihooks[id].syscall, g_apihooks[id].func); + } + else + { + apiHookAddr(g_apihooks[id].syscall, g_apihooks[id].func); + } + g_apihooks[id].func = NULL; + } + + pspSdkEnableInterrupts(intc); +} + + +static int _apiHookGenericCommon(unsigned int addr, int imphook, const char *library, const char *name, char ret, const char *format, int sleep) +{ + int id; + unsigned int *syscall; + unsigned int *target; + unsigned int *hookaddr = (unsigned int*) _apiHook0; + int result = 0; + + do + { + id = find_free_hook(); + if(id < 0) + { + SHELL_PRINT("No free API hooks left\n"); + break; + } + + if(!addr) + { + SHELL_PRINT("Couldn't find export address\n"); + break; + } + + if(imphook) + { + unsigned int *p = (unsigned int*) addr; + int user; + + user = (addr & 0x80000000) ? 0 : 1; + if(user) + { + if(g_userdispatch == NULL) + { + SHELL_PRINT("Cannot hook user calls without psplink_user\n"); + break; + } + hookaddr = g_userdispatch; + } + + if((*p & ~0x03FFFFFF) != 0x08000000) + { + SHELL_PRINT("Cannot hook syscall imports\n"); + break; + } + + syscall = (void*) addr; + target = (void*) (((*p & 0x03FFFFFF) << 2) | (addr & 0xF0000000)); + } + else + { + syscall = find_syscall_addr(addr); + if(!syscall) + { + SHELL_PRINT("Couldn't find syscall address\n"); + break; + } + target = (void*) addr; + } + + g_apihooks[id].syscall = syscall; + g_apihooks[id].func = target; + g_apihooks[id].ret = ret; + g_apihooks[id].sleep = sleep; + strncpy(g_apihooks[id].param, format, APIHOOK_MAXPARAM); + strncpy(g_apihooks[id].name, name, APIHOOK_MAXNAME); + g_apihooks[id].name[APIHOOK_MAXNAME-1] = 0; + g_apihooks[id].imphook = imphook; + if(imphook) + { + apiHookImport(syscall, &hookaddr[id*2]); + } + else + { + apiHookAddr(syscall, &hookaddr[id*2]); + } + result = 1; + } + while(0); + + return result; +} + +int apiHookGenericByName(SceUID uid, const char *library, const char *name, char ret, const char *format, int sleep) +{ + unsigned int addr; + int imphook = 0; + + addr = libsFindExportByName(uid, library, name); + if(!addr) + { + /* If not an export try and import */ + addr = libsFindImportAddrByName(uid, library, name); + imphook = 1; + } + + return _apiHookGenericCommon(addr, imphook, library, name, ret, format, sleep); +} + +int apiHookGenericByNid(SceUID uid, const char *library, unsigned int nid, char ret, const char *format, int sleep) +{ + unsigned int addr; + int imphook = 0; + char name[APIHOOK_MAXNAME]; + + addr = libsFindExportByNid(uid, library, nid); + if(!addr) + { + addr = libsFindImportAddrByNid(uid, library, nid); + imphook = 1; + } + + sprintf(name, "Nid:0x%08X", nid); + + return _apiHookGenericCommon(addr, imphook, library, name, ret, format, sleep); +} + + +unsigned int apiHookByName(SceUID uid, const char *library, const char *name, void *func) +{ + unsigned int addr; + + addr = libsFindExportByName(uid, library, name); + if(addr) + { + if(!apiHookAddr(find_syscall_addr(addr), func)) + { + addr = 0; + } + } + else + { + addr = libsFindImportAddrByName(uid, library, name); + if(addr) + { + if(!apiHookImport((unsigned int*) addr, func)) + { + addr = 0; + } + } + } + + return addr; +} + +unsigned int apiHookByNid(SceUID uid, const char *library, unsigned int nid, void *func) +{ + unsigned int addr; + + addr = libsFindExportByNid(uid, library, nid); + if(addr) + { + if(!apiHookAddr(find_syscall_addr(addr), func)) + { + addr = 0; + } + } + else + { + addr = libsFindImportAddrByNid(uid, library, nid); + if(addr) + { + if(!apiHookImport((unsigned int*) addr, func)) + { + addr = 0; + } + } + } + + return addr; +} diff --git a/psplink/apihook.h b/psplink/apihook.h new file mode 100644 index 0000000..bab9280 --- /dev/null +++ b/psplink/apihook.h @@ -0,0 +1,23 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * apihook.h - User mode API Hooking for psplink + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/apihook.h $ + * $Id: apihook.h 2301 2007-08-26 13:48:05Z tyranid $ + */ +#ifndef __APIHOOK_H__ +#define __APIHOOK_H__ + +unsigned int apiHookByName(SceUID uid, const char *library, const char *name, void *func); +unsigned int apiHookByNid(SceUID uid, const char *library, unsigned int nid, void *func); +int apiHookGenericByName(SceUID uid, const char *library, const char *name, char ret, const char *format, int sleep); +int apiHookGenericByNid(SceUID uid, const char *library, unsigned int nid, char ret, const char *format, int sleep); +void apiHookGenericDelete(int id); +void apiHookGenericPrint(void); + +#endif diff --git a/psplink/bitmap.c b/psplink/bitmap.c new file mode 100644 index 0000000..a024a0e --- /dev/null +++ b/psplink/bitmap.c @@ -0,0 +1,201 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * bitmap.c - PSPLINK kernel module bitmap code + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/bitmap.c $ + * $Id: bitmap.c 2026 2006-10-14 13:09:48Z tyranid $ + */ + +#include +#include +#include +#include +#include +#include "psplink.h" + +#define BMP_ID "BM" + +struct BitmapHeader +{ + char id[2]; + uint32_t filesize; + uint32_t reserved; + uint32_t offset; + uint32_t headsize; + uint32_t width; + uint32_t height; + uint16_t planes; + uint16_t bpp; + uint32_t comp; + uint32_t bitmapsize; + uint32_t hres; + uint32_t vres; + uint32_t colors; + uint32_t impcolors; +} __attribute__((packed)); + +static int fixed_write(int fd, void *data, int len) +{ + int writelen = 0; + + while(writelen < len) + { + int ret; + + ret = sceIoWrite(fd, data + writelen, len - writelen); + if(ret <= 0) + { + writelen = -1; + break; + } + writelen += ret; + } + + return writelen; +} + +int write_8888_data(void *frame, void *pixel_data) +{ + uint8_t *line; + uint8_t *p; + int i; + int h; + + line = pixel_data; + for(h = 271; h >= 0; h--) + { + p = frame + (h*512*4); + for(i = 0; i < 480; i++) + { + line[(i*3) + 2] = p[i*4]; + line[(i*3) + 1] = p[(i*4) + 1]; + line[(i*3) + 0] = p[(i*4) + 2]; + } + line += 480 * 3; + } + + return 0; +} + +int write_5551_data(void *frame, void *pixel_data) +{ + uint8_t *line; + uint16_t *p; + int i; + int h; + + line = pixel_data; + for(h = 271; h >= 0; h--) + { + p = frame; + p += (h * 512); + for(i = 0; i < 480; i++) + { + line[(i*3) + 2] = (p[i] & 0x1F) << 3; + line[(i*3) + 1] = ((p[i] >> 5) & 0x1F) << 3; + line[(i*3) + 0] = ((p[i] >> 10) & 0x1F) << 3; + } + line += 480*3; + } + + return 0; +} + +int write_565_data(void *frame, void *pixel_data) +{ + uint8_t *line; + uint16_t *p; + int i; + int h; + + line = pixel_data; + for(h = 271; h >= 0; h--) + { + p = frame; + p += (h * 512); + for(i = 0; i < 480; i++) + { + line[(i*3) + 2] = (p[i] & 0x1F) << 3; + line[(i*3) + 1] = ((p[i] >> 5) & 0x3F) << 2; + line[(i*3) + 0] = ((p[i] >> 11) & 0x1F) << 3; + } + line += 480*3; + } + + return 0; +} + +int write_4444_data(void *frame, void *pixel_data) +{ + uint8_t *line; + uint16_t *p; + int i; + int h; + + line = pixel_data; + for(h = 271; h >= 0; h--) + { + p = frame; + p += (h * 512); + for(i = 0; i < 480; i++) + { + line[(i*3) + 2] = (p[i] & 0xF) << 4; + line[(i*3) + 1] = ((p[i] >> 4) & 0xF) << 4; + line[(i*3) + 0] = ((p[i] >> 8) & 0xF) << 4; + } + line += 480*3; + } + + return 0; +} + +int bitmapWrite(void *frame_addr, void *tmp_buf, int format, const char *file) +{ + struct BitmapHeader *bmp; + void *pixel_data = tmp_buf + sizeof(struct BitmapHeader); + int fd; + + bmp = (struct BitmapHeader *) tmp_buf; + memset(bmp, 0, sizeof(struct BitmapHeader)); + memcpy(bmp->id, BMP_ID, sizeof(bmp->id)); + bmp->filesize = 480*272*3 + sizeof(struct BitmapHeader); + bmp->offset = sizeof(struct BitmapHeader); + bmp->headsize = 0x28; + bmp->width = 480; + bmp->height = 272; + bmp->planes = 1; + bmp->bpp = 24; + bmp->bitmapsize = 480*272*3; + bmp->hres = 2834; + bmp->vres = 2834; + + fd = sceIoOpen(file, PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0777); + if(fd < 0) + { + SHELL_PRINT("Could not open file '%s' for writing\n", file); + return -1; + } + + switch(format) + { + case PSP_DISPLAY_PIXEL_FORMAT_565: write_565_data(frame_addr, pixel_data); + break; + case PSP_DISPLAY_PIXEL_FORMAT_5551: write_5551_data(frame_addr, pixel_data); + break; + case PSP_DISPLAY_PIXEL_FORMAT_4444: write_4444_data(frame_addr, pixel_data); + break; + case PSP_DISPLAY_PIXEL_FORMAT_8888: write_8888_data(frame_addr, pixel_data); + break; + }; + + fixed_write(fd, tmp_buf, bmp->filesize); + + sceIoClose(fd); + + return 0; +} diff --git a/psplink/bitmap.h b/psplink/bitmap.h new file mode 100644 index 0000000..706b3fd --- /dev/null +++ b/psplink/bitmap.h @@ -0,0 +1,19 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * bitmap.h - PSPLINK kernel module bitmap code + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/bitmap.h $ + * $Id: bitmap.h 1622 2005-12-27 19:29:57Z tyranid $ + */ + +#ifndef __BITMAP_H__ +#define __BITMAP_H__ + +int bitmapWrite(void *frame_addr, void *tmp_buf, int format, const char *file); + +#endif diff --git a/psplink/config.c b/psplink/config.c new file mode 100644 index 0000000..2fe805b --- /dev/null +++ b/psplink/config.c @@ -0,0 +1,207 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * config.c - PSPLINK kernel module configuration loader. + * + * Copyright (c) 2005 James F + * Copyright (c) 2005 Julian T + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/config.c $ + * $Id: config.c 2174 2007-02-08 19:49:27Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "memoryUID.h" +#include "psplink.h" +#include "psplinkcnf.h" +#include "util.h" +#include "shell.h" +#include "config.h" + +struct psplink_config +{ + const char *name; + int isnum; + void (*handler)(struct ConfigContext *ctx, const char *szVal, unsigned int iVal); +}; + +static void config_pluser(struct ConfigContext *ctx, const char *szVal, unsigned int iVal) +{ + ctx->enableuser = iVal; +} + +static void config_resetonexit(struct ConfigContext *ctx, const char *szVal, unsigned int iVal) +{ + ctx->resetonexit = iVal; +} + +static void config_pid(struct ConfigContext *ctx, const char *szVal, unsigned int iVal) +{ + ctx->pid = iVal; +} + +struct psplink_config config_names[] = { + { "pluser", 1, config_pluser }, + { "resetonexit", 1, config_resetonexit }, + { "pid", 1, config_pid }, + { NULL, 0, NULL } +}; + +void configLoad(const char *bootpath, struct ConfigContext *ctx) +{ + char cnf_path[256]; + struct ConfigFile cnf; + + memset(ctx, 0, sizeof(*ctx)); + strcpy(cnf_path, bootpath); + strcat(cnf_path, "psplink.ini"); + Kprintf("Config Path %s\n", cnf_path); + if(psplinkConfigOpen(cnf_path, &cnf)) + { + const char *name; + const char *val; + + while((val = psplinkConfigReadNext(&cnf, &name))) + { + int config; + + config = 0; + while(config_names[config].name) + { + if(strcmp(config_names[config].name, name) == 0) + { + unsigned int iVal = 0; + if(config_names[config].isnum) + { + char *endp; + + iVal = strtoul(val, &endp, 0); + if(*endp != 0) + { + Kprintf("Error, line %d value should be a number\n", cnf.line); + break; + } + } + + config_names[config].handler(ctx, val, iVal); + } + config++; + } + + /* Ignore anything we don't care about */ + } + + psplinkConfigClose(&cnf); + } +} + +void configPrint(const char *bootpath) +{ + char cnf_path[256]; + struct ConfigFile cnf; + + strcpy(cnf_path, bootpath); + strcat(cnf_path, "psplink.ini"); + SHELL_PRINT("Config Path %s\n", cnf_path); + if(psplinkConfigOpen(cnf_path, &cnf)) + { + const char *name; + const char *val; + + while((val = psplinkConfigReadNext(&cnf, &name))) + { + SHELL_PRINT("%s=%s\n", name, val); + } + + psplinkConfigClose(&cnf); + } +} + +void configChange(const char *bootpath, const char *newname, const char *newval, int mode) +{ + char cnf_path[256]; + char new_path[256]; + int found = 0; + struct ConfigFile cnf; + int fd = -1; + + if((mode != CONFIG_MODE_ADD) && (mode != CONFIG_MODE_DEL)) + { + return; + } + + strcpy(cnf_path, bootpath); + strcat(cnf_path, "psplink.ini"); + SHELL_PRINT("Config Path %s\n", cnf_path); + + strcpy(new_path, bootpath); + strcat(new_path, "psplink.ini.tmp"); + fd = sceIoOpen(new_path, PSP_O_WRONLY | PSP_O_TRUNC | PSP_O_CREAT, 0777); + if(fd >= 0) + { + if(psplinkConfigOpen(cnf_path, &cnf)) + { + const char *name; + const char *val; + + while((val = psplinkConfigReadNext(&cnf, &name))) + { + if(strcmp(name, newname) == 0) + { + if(mode == CONFIG_MODE_ADD) + { + fdprintf(fd, "%s=\"%s\"\n", newname, newval); + found = 1; + } + } + else + { + fdprintf(fd, "%s=\"%s\"\n", name, val); + } + } + + if((mode == CONFIG_MODE_ADD) && (!found)) + { + fdprintf(fd, "%s=\"%s\"\n", newname, newval); + } + + sceIoClose(fd); + fd = -1; + psplinkConfigClose(&cnf); + + if(sceIoRemove(cnf_path) < 0) + { + SHELL_PRINT("Error deleting original configuration\n"); + } + else + { + if(sceIoRename(new_path, cnf_path) < 0) + { + SHELL_PRINT("Error renaming configuration\n"); + } + } + } + else + { + SHELL_PRINT("Couldn't open temporary config file %s\n", new_path); + } + + if(fd >= 0) + { + sceIoClose(fd); + sceIoRemove(new_path); + } + } +} diff --git a/psplink/config.h b/psplink/config.h new file mode 100644 index 0000000..ef0eee7 --- /dev/null +++ b/psplink/config.h @@ -0,0 +1,33 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * config.h - PSPLINK kernel module configuration loader. + * + * Copyright (c) 2005 James F + * Copyright (c) 2005 Julian T + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/config.h $ + * $Id: config.h 2148 2007-01-23 18:04:34Z tyranid $ + */ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#define CONFIG_MODE_ADD 1 +#define CONFIG_MODE_DEL 2 + +struct ConfigContext +{ + /* Indicates whether to enable the psplink user module */ + int enableuser; + int resetonexit; + int pid; +}; + +void configLoad(const char *bootpath, struct ConfigContext *ctx); +void configPrint(const char *bootpath); +void configChange(const char *bootpath, const char *name, const char *val, int mode); + +#endif diff --git a/psplink/debug.c b/psplink/debug.c new file mode 100644 index 0000000..dda4b10 --- /dev/null +++ b/psplink/debug.c @@ -0,0 +1,868 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * debug.c - Debugger code for psplink. + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/debug.c $ + * $Id: debug.c 2301 2007-08-26 13:48:05Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include "exception.h" +#include "util.h" +#include "psplink.h" +#include "shellcmd.h" +#include "debug.h" +#include "decodeaddr.h" +#include "thctx.h" + +#define SW_BREAK_INST 0x0000000d + +extern struct GlobalContext g_context; + +struct HwDebugEnv +{ + unsigned int flags; + unsigned int DRCNTL; + unsigned int IBC; + unsigned int DBC; + unsigned int IBA; + unsigned int IBAM; + unsigned int DBA; + unsigned int DBAM; + unsigned int DBD; + unsigned int DBDM; +} psplinkHwContext; + +int debugGetEnv(struct HwDebugEnv *env); + +typedef struct _DebugEvent +{ + SceKernelMsgPacket header; + struct PsplinkContext *ctx; +} DebugEvent; + + +static struct Breakpoint g_bps[DEBUG_MAX_BPS]; +static struct _DebugEventHandler* g_eventhead; +extern const char *regName[32]; + +static void debug_set_hwbreak(unsigned int addr); + +int debugRegisterEventHandler(DebugEventHandler *handler) +{ + int intc; + + if(!handler || (handler->size != sizeof(DebugEventHandler)) || (handler->mbox < 0)) + { + return -1; + } + + if(handler->membase > handler->memtop) + { + return -1; + } + + intc = pspSdkDisableInterrupts(); + handler->pNext = g_eventhead; + g_eventhead = handler; + pspSdkEnableInterrupts(intc); + + return 0; +} + +int debugUnregisterEventHandler(DebugEventHandler *handler) +{ + DebugEventHandler *ev; + int intc; + + intc = pspSdkDisableInterrupts(); + if(g_eventhead == handler) + { + g_eventhead = g_eventhead->pNext; + } + else + { + ev = g_eventhead; + while(ev && (ev->pNext != handler)) + { + ev = ev->pNext; + } + if(ev) + { + ev->pNext = ev->pNext->pNext; + } + } + pspSdkEnableInterrupts(intc); + + return 0; +} + +int debugWaitDebugEvent(DebugEventHandler *handler, struct PsplinkContext **ctx, SceUInt *timeout) +{ + int ret; + DebugEvent *ev; + void *p; + + if(!handler || !ctx) + { + return -1; + } + + ret = sceKernelReceiveMbx(handler->mbox, &p, timeout); + if(ret == 0) + { + ev = (DebugEvent*) p; + *ctx = ev->ctx; + } + + return ret; +} + +/* Define some opcode stuff for the stepping function */ +#define BEQ_OPCODE 0x4 +#define BEQL_OPCODE 0x14 +#define BGTZ_OPCODE 0x7 +#define BGTZL_OPCODE 0x17 +#define BLEZ_OPCODE 0x6 +#define BLEZL_OPCODE 0x16 +#define BNE_OPCODE 0x5 +#define BNEL_OPCODE 0x15 + +/* Reg Imm */ +#define REGIMM_OPCODE 0x1 +#define BGEZ_OPCODE 0x1 +#define BGEZAL_OPCODE 0x11 +#define BGEZALL_OPCODE 0x13 +#define BGEZL_OPCODE 0x3 +#define BLTZ_OPCODE 0 +#define BLTZAL_OPCODE 0x10 +#define BLTZALL_OPCODE 0x12 +#define BLTZL_OPCODE 0x2 + +#define J_OPCODE 0x2 +#define JAL_OPCODE 0x3 + +/* Special opcode */ +#define SPECIAL_OPCODE 0 +#define JALR_OPCODE 0x9 +#define JR_OPCODE 0x8 +#define SYSCALL_OPCODE 0xc + +/* Cop Branches (all the same) */ +#define COP0_OPCODE 0x10 +#define COP1_OPCODE 0x11 +#define COP2_OPCODE 0x12 +#define BCXF_OPCODE 0x100 +#define BCXFL_OPCODE 0x102 +#define BCXT_OPCODE 0x101 +#define BCXTL_OPCODE 0x103 + +/* Generic step command , if skip then will try to skip over jals */ +static void step_generic(struct PsplinkContext *ctx, int skip) +{ + unsigned int opcode; + unsigned int epc; + unsigned int targetpc; + int branch = 0; + int cond = 0; + int link = 0; + + epc = ctx->regs.epc; + targetpc = epc + 4; + + opcode = _lw(epc); + + switch(opcode >> 26) + { + case BEQ_OPCODE: + case BEQL_OPCODE: + case BGTZ_OPCODE: + case BGTZL_OPCODE: + case BLEZ_OPCODE: + case BLEZL_OPCODE: + case BNE_OPCODE: + case BNEL_OPCODE: + { + short ofs; + + ofs = (short) (opcode & 0xffff); + cond = 1; + branch = 1; + targetpc += ofs * 4; + } + break; + case REGIMM_OPCODE: { + switch((opcode >> 16) & 0x1f) + { + case BGEZ_OPCODE: + case BGEZAL_OPCODE: + case BGEZALL_OPCODE: + case BGEZL_OPCODE: + case BLTZ_OPCODE: + case BLTZAL_OPCODE: + case BLTZALL_OPCODE: + case BLTZL_OPCODE: { + short ofs; + + ofs = (short) (opcode & 0xffff); + cond = 1; + branch = 1; + targetpc += ofs * 4; + } + break; + } + } + break; + case JAL_OPCODE: link = 1; + case J_OPCODE: { + unsigned int ofs; + + ofs = opcode & 0x3ffffff; + targetpc = (ofs << 2) | (targetpc & 0xf0000000); + branch = 1; + cond = 0; + } + break; + case SPECIAL_OPCODE: + { + switch(opcode & 0x3f) + { + case JALR_OPCODE: link = 1; + case JR_OPCODE: + { + unsigned int rs; + + rs = (opcode >> 21) & 0x1f; + targetpc = ctx->regs.r[rs]; + branch = 1; + cond = 0; + } + break; + }; + } + break; + case COP0_OPCODE: + case COP1_OPCODE: + case COP2_OPCODE: + { + switch((opcode >> 16) & 0x3ff) + { + case BCXF_OPCODE: + case BCXFL_OPCODE: + case BCXT_OPCODE: + case BCXTL_OPCODE: + { + short ofs; + + ofs = (short) (opcode & 0xffff); + cond = 1; + branch = 1; + targetpc += ofs * 4; + } + break; + }; + } + break; + }; + + if(link && skip) + { + debugSetBP(epc+8, DEBUG_BP_ONESHOT | DEBUG_BP_STEP, ctx->thid); + } + else if(branch) + { + struct Breakpoint *bp1; + struct Breakpoint *bp2; + + bp1 = debugSetBP(targetpc, DEBUG_BP_ONESHOT | DEBUG_BP_STEP, ctx->thid); + + if((cond) && (targetpc != (epc + 8))) + { + bp2 = debugSetBP(epc+8, DEBUG_BP_ONESHOT | DEBUG_BP_STEP, ctx->thid); + if(bp1) + { + bp1->step = bp2; + } + if(bp2) + { + bp2->step = bp1; + } + } + } + else + { + debugSetBP(targetpc, DEBUG_BP_ONESHOT | DEBUG_BP_STEP, ctx->thid); + } +} + +void debugStep(struct PsplinkContext *ctx, int skip) +{ + step_generic(ctx, skip); + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); +} + +static struct Breakpoint *find_bp(unsigned int address) +{ + int i; + + /* Mask out top nibble so we match whether we end up in kmem, + * user mem or cached mem */ + address &= 0x0FFFFFFF; + for(i = 0; i < DEBUG_MAX_BPS; i++) + { + if((g_bps[i].flags & DEBUG_BP_ACTIVE) && ((g_bps[i].addr & 0x0FFFFFFF) == address)) + { + return &g_bps[i]; + } + } + + return NULL; +} + +static struct Breakpoint *find_hwbp(void) +{ + int i; + + for(i = 0; i < DEBUG_MAX_BPS; i++) + { + if((g_bps[i].flags & DEBUG_BP_ACTIVE) && (g_bps[i].flags & DEBUG_BP_HARDWARE)) + { + return &g_bps[i]; + } + } + + return NULL; +} + +static struct Breakpoint *find_freebp(void) +{ + int i; + for(i = 0; i < DEBUG_MAX_BPS; i++) + { + if((g_bps[i].flags & DEBUG_BP_ACTIVE) == 0) + { + return &g_bps[i]; + } + } + + return NULL; +} + +struct Breakpoint* debugSetBP(unsigned int address, unsigned int flags, SceUID thid) +{ + if(find_bp(address) == NULL) + { + struct Breakpoint *pBp = NULL; + + if(flags & DEBUG_BP_HARDWARE) + { + /* Check for existing hardware breakpoint */ + if(find_hwbp()) + { + return NULL; + } + } + + pBp = find_freebp(); + if(pBp != NULL) + { + memset(pBp, 0, sizeof(struct Breakpoint)); + pBp->inst = _lw(address); + + if(flags & DEBUG_BP_HARDWARE) + { + debug_set_hwbreak(address); + } + else + { + _sw(SW_BREAK_INST, address); + } + + pBp->addr = address; + pBp->flags = DEBUG_BP_ACTIVE | flags; + pBp->thid = thid; + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + + return pBp; + } + else + { + SHELL_PRINT("Error, could not find a free breakpoint\n"); + } + } + + return NULL; +} + +struct Breakpoint* debugFindBPByIndex(int i) +{ + if((i >= 0) && (i < DEBUG_MAX_BPS)) + { + if(g_bps[i].flags & DEBUG_BP_ACTIVE) + { + return &g_bps[i]; + } + } + + return NULL; +} + +int debugDeleteBP(unsigned int addr) +{ + int ret = 0; + int intc; + struct Breakpoint *pBp; + + intc = pspSdkDisableInterrupts(); + pBp = find_bp(addr); + if(pBp) + { + if((pBp->flags & DEBUG_BP_HARDWARE) == 0) + { + _sw(pBp->inst, pBp->addr); + } + else + { + debug_set_hwbreak(0); + } + + pBp->flags = 0; + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + ret = 1; + } + pspSdkEnableInterrupts(intc); + + return ret; +} + +int debugDisableBP(unsigned int addr) +{ + int ret = 0; + int intc; + struct Breakpoint *pBp; + + intc = pspSdkDisableInterrupts(); + pBp = find_bp(addr); + if(pBp) + { + if((pBp->flags & DEBUG_BP_HARDWARE) == 0) + { + _sw(pBp->inst, pBp->addr); + } + else + { + debug_set_hwbreak(0); + } + + pBp->flags |= DEBUG_BP_DISABLED; + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + ret = 1; + } + pspSdkEnableInterrupts(intc); + + return ret; +} + +int debugEnableBP(unsigned int addr) +{ + int ret = 0; + int intc; + struct Breakpoint *pBp; + + intc = pspSdkDisableInterrupts(); + pBp = find_bp(addr); + if(pBp) + { + if((pBp->flags & DEBUG_BP_HARDWARE) == 0) + { + _sw(SW_BREAK_INST, pBp->addr); + } + else + { + debug_set_hwbreak(pBp->addr); + } + + pBp->flags &= ~(DEBUG_BP_DISABLED | DEBUG_BP_NEXT_REENABLE); + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + ret = 1; + } + pspSdkEnableInterrupts(intc); + + return ret; +} + +void debugPrintBPs(void) +{ + int i; + + SHELL_PRINT("Breakpoint List:\n"); + for(i = 0; i < DEBUG_MAX_BPS; i++) + { + if(g_bps[i].flags & DEBUG_BP_ACTIVE) + { + SHELL_PRINT("%-2d: Addr:0x%08X Inst:0x%08X Flags:%c%c%c\n", i, g_bps[i].addr, g_bps[i].inst, + g_bps[i].flags & DEBUG_BP_ONESHOT ? 'O' : '-', g_bps[i].flags & DEBUG_BP_HARDWARE ? 'H' : '-', + g_bps[i].flags & DEBUG_BP_DISABLED ? 'D' : '-'); + } + } +} + +void debugClearException(struct PsplinkContext *ctx) +{ + unsigned int address; + struct Breakpoint *pBp; + + if(ctx->regs.type == PSPLINK_EXTYPE_DEBUG) + { + ctx->drcntl = psplinkHwContext.DRCNTL; + } + + address = ctx->regs.epc; + + /* Adjust for delay slot */ + if(ctx->regs.cause & 0x80000000) + { + address += 4; + } + ctx->error = 1; + + pBp = find_bp(address); + if((pBp != NULL) && ((pBp->flags & DEBUG_BP_DISABLED) == 0)) + { + if(pBp->flags & DEBUG_BP_ONESHOT) + { + struct Breakpoint *step; + + step = pBp->step; + debugDeleteBP(pBp->addr); + + if(step) + { + debugDeleteBP(step->addr); + } + } + + ctx->error = 0; + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + } +} + +int debugCheckThread(struct PsplinkContext *ctx, unsigned int *addr) +{ + SceKernelThreadInfo thread; + + memset(&thread, 0, sizeof(thread)); + thread.size = sizeof(thread); + if(!sceKernelReferThreadStatus(ctx->thid, &thread)) + { + *addr = (unsigned int) thread.entry; + + if(strcmp(thread.name, "PspLink") == 0) + { + /* Indicate we are in the psplink parse thread */ + return 1; + } + } + + return 0; +} + +int debugBreakThread(SceUID uid) +{ + int intc; + unsigned int addr; + int ret = -1; + + intc = pspSdkDisableInterrupts(); + addr = thGetCurrentEPC(uid); + if(addr) + { + if(debugSetBP(addr, DEBUG_BP_ONESHOT, uid)) + { + ret = 0; + } + } + pspSdkEnableInterrupts(intc); + + return ret; +} + +int debugHandleException(struct PsplinkContext *ctx) +{ + int ret = 0; + unsigned int addr; + int initex; + struct _DebugEventHandler *ev; + + /* Should have an indication that this was a step */ + /* This is where we need to loop around our debug handlers and post messages */ + initex = debugCheckThread(ctx, &addr); + + /* Even if the exception happened in the psplink thread then at least see if any debuggers + * are interested in us (so we _could_ debug outselves at some point) */ + ctx->cont = PSP_EXCEPTION_NOT_HANDLED; + + ev = g_eventhead; + while(ev) + { + DebugEvent post; + + if((addr >= ev->membase) && (addr <= ev->memtop)) + { + memset(&post, 0, sizeof(post)); + post.ctx = ctx; + if(sceKernelSendMbx(ev->mbox, &post) == 0) + { + sceKernelSleepThread(); + if(ctx->cont != PSP_EXCEPTION_NOT_HANDLED) + { + break; + } + } + } + ev = ev->pNext; + } + + if(ctx->cont == PSP_EXCEPTION_NOT_HANDLED) + { + if(ctx->error) + { + exceptionPrint(ctx); + } + + if(((ctx->regs.epc & 3) == 0) && (memValidate(ctx->regs.epc, MEM_ATTRIB_READ | MEM_ATTRIB_WORD))) + { + SHELL_PRINT_CMD(SHELL_CMD_DISASM, "0x%08X:0x%08X", ctx->regs.epc, _lw(ctx->regs.epc)); + } + + /* If this was not our parse thread */ + if(!initex) + { + sceKernelSleepThread(); + } + else + { + /* Setup return context for our thread */ + ctx->regs.epc = (unsigned int) longjmp; + ctx->regs.r[4] = (unsigned int) g_context.parseenv; + ctx->regs.r[5] = 1; + ctx->cont = PSP_EXCEPTION_CONTINUE; + } + } + + return ret; +} + +void debugEnableHW(void) +{ + asm( + "mfc0 $t0, $12\n" + "lui $t1, 8\n" + "or $t1, $t1, $t0\n" + "mtc0 $t1, $12\n" + ); +} + +void debugDisableHW(void) +{ + asm( + "mfc0 $t0, $12\n" + "lui $t1, 8\n" + "not $t1, $t1\n" + "and $t1, $t1, $t0\n" + "mtc0 $t1, $12\n" + ); +} + +int debugHWEnabled(void) +{ + int ret = 0; + asm( + "mfc0 $t0, $12\n" + "lui $t1, 8\n" + "and %0, $t1, $t0\n" + : "=r"(ret) + ); + + return ret; +} + +static void debug_get_env(void) +{ + psplinkHwContext.flags = 1; + asm( + "dbreak\n" + "nop\n" + ); +} + +static void debug_set_env(void) +{ + psplinkHwContext.flags = 2; + asm( + "dbreak\n" + "nop\n" + ); +} + +extern unsigned int psplinkHwDebugTrap; +extern unsigned int psplinkHwDebugTrapEnd; +void psplinkHwDebugMain(void); + +void debugHwInit(void) +{ + unsigned int* p = &psplinkHwDebugTrap; + unsigned int base = 0xbfc01000; + while(p < &psplinkHwDebugTrapEnd) + { + _sw(*p, base); + base += 4; + p++; + } + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + asm __volatile__ ( + "ctc0 %0, $10\n" + : : "r"(psplinkHwDebugMain) + ); + debugEnableHW(); + memset(&psplinkHwContext, 0, sizeof(psplinkHwContext)); + debug_set_env(); +} + +static void debug_set_hwbreak(unsigned int addr) +{ + struct HwDebugEnv *pEnv = &psplinkHwContext; + + debug_get_env(); + + if(addr) + { + pEnv->IBC = 0x12; + pEnv->IBA = addr; + pEnv->IBAM = 0; + } + else + { + pEnv->IBC = 0x10; + pEnv->IBA = 0; + pEnv->IBAM = 0; + } + + debug_set_env(); +} + +/* + +void debugSetHWRegs(int argc, char **argv) +{ + int i; + struct HwDebugEnv *pEnv; + + pEnv = debug_get_env(); + if(pEnv == NULL) + { + return; + } + + for(i = 0; i < argc; i++) + { + char *pEquals; + + pEquals = strchr(argv[i], '='); + if(pEquals) + { + unsigned int val; + *pEquals = 0; + pEquals++; + if(memDecode(pEquals, &val)) + { + if(strcmp(argv[i], "IBC") == 0) + { + pEnv->IBC = val; + } + else if(strcmp(argv[i], "DBC") == 0) + { + pEnv->DBC = val; + } + else if(strcmp(argv[i], "IBA") == 0) + { + pEnv->IBA = val; + } + else if(strcmp(argv[i], "IBAM") == 0) + { + pEnv->IBAM = val; + } + else if(strcmp(argv[i], "DBA") == 0) + { + pEnv->DBA = val; + } + else if(strcmp(argv[i], "DBAM") == 0) + { + pEnv->DBAM = val; + } + else if(strcmp(argv[i], "DBD") == 0) + { + pEnv->DBD = val; + } + else if(strcmp(argv[i], "DBDM") == 0) + { + pEnv->DBDM = val; + } + else + { + SHELL_PRINT("Unknown register %s\n", argv[i]); + return; + } + + debug_set_env(); + } + else + { + SHELL_PRINT("Invalid memory specification %s\n", pEquals); + return; + } + + } + else + { + SHELL_PRINT("Invalid register specification %s\n", argv[i]); + return; + } + } +} + +*/ + +void debugPrintHWRegs(void) +{ + struct HwDebugEnv *pEnv = &psplinkHwContext; + + debug_get_env(); + SHELL_PRINT("\n"); + SHELL_PRINT("%-6s: 0x%08X\n", "DRCNTL", pEnv->DRCNTL); + SHELL_PRINT("%-6s: 0x%08X\n", "IBC", pEnv->IBC); + SHELL_PRINT("%-6s: 0x%08X\n", "DBC", pEnv->DBC); + SHELL_PRINT("%-6s: 0x%08X\n", "IBA", pEnv->IBA); + SHELL_PRINT("%-6s: 0x%08X\n", "IBAM", pEnv->IBAM); + SHELL_PRINT("%-6s: 0x%08X\n", "DBA", pEnv->DBA); + SHELL_PRINT("%-6s: 0x%08X\n", "DBAM", pEnv->DBAM); + SHELL_PRINT("%-6s: 0x%08X\n", "DBD", pEnv->DBD); + SHELL_PRINT("%-6s: 0x%08X\n", "DBDM", pEnv->DBDM); +} diff --git a/psplink/debug.h b/psplink/debug.h new file mode 100644 index 0000000..32f3e68 --- /dev/null +++ b/psplink/debug.h @@ -0,0 +1,81 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * debug.h - Debugger code for psplink + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/debug.h $ + * $Id: debug.h 2178 2007-02-15 17:30:31Z tyranid $ + */ +#ifndef __DEBUGINC_H__ +#define __DEBUGINC_H__ + +#include +#include "exception.h" + +#define DEBUG_MAX_BPS 32 + +enum DebugBreakpointFlags +{ + /* Indicates this breakpoint is active */ + DEBUG_BP_ACTIVE = 1, + /* Indicates once this breakpoint is hit we remove it */ + DEBUG_BP_ONESHOT = 2, + /* Indicates this is a hardware breakpoint */ + DEBUG_BP_HARDWARE = 4, + /* Indicates this breakpoint is current disabled by the user */ + DEBUG_BP_DISABLED = 8, + /* Used as a visual flag to indicate this breakpoint is a step */ + DEBUG_BP_STEP = 16, + /* Indicates this breakpoint should be reneabled on the next exception */ + DEBUG_BP_NEXT_REENABLE = 32, +}; + +struct Breakpoint +{ + /* Address of breakpoint */ + unsigned int addr; + /* Original instruction at addr */ + unsigned int inst; + /* Flags */ + unsigned int flags; + /* Are we waiting for a specific thread id ? */ + SceUID thid; + /* Link to the corresponding step breakpoint */ + struct Breakpoint* step; +}; + +typedef struct _DebugEventHandler +{ + union { + int size; + struct _DebugEventHandler *pNext; + }; + unsigned int membase; + unsigned int memtop; + SceUID mbox; +} DebugEventHandler; + +int debugRegisterEventHandler(DebugEventHandler *handler); +int debugUnregisterEventHandler(DebugEventHandler *handler); +int debugWaitDebugEvent(DebugEventHandler *handler, struct PsplinkContext **ctx, SceUInt *timeout); +void debugPrintBPs(void); +struct Breakpoint* debugFindBPByIndex(int i); +int debugDeleteBP(unsigned int addr); +int debugDisableBP(unsigned int addr); +int debugEnableBP(unsigned int addr); +struct Breakpoint *debugSetBP(unsigned int address, unsigned int flags, SceUID thid); +int debugBreakThread(SceUID uid); +void debugDisableHW(); + +/* For internal use only */ +void debugStep(struct PsplinkContext *ctx, int skip); +void debugClearException(struct PsplinkContext *ctx); +int debugHandleException(struct PsplinkContext *ctx); +void debugHwInit(void); +void debugPrintHWRegs(void); + +#endif diff --git a/psplink/decodeaddr.c b/psplink/decodeaddr.c new file mode 100644 index 0000000..b4b93b1 --- /dev/null +++ b/psplink/decodeaddr.c @@ -0,0 +1,661 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * decodeaddr.c - PSPLINK kernel module decode memory address code + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/decodeaddr.c $ + * $Id: decodeaddr.c 2301 2007-08-26 13:48:05Z tyranid $ + */ + +#include +#include +#include +#include +#include +#include +#include "psplink.h" +#include "decodeaddr.h" +#include "exception.h" +#include "util.h" +#include "libs.h" + +/* Indicates that memory protection is on */ +static int g_protoff = 0; + +struct mem_entry +{ + unsigned int addr; + s32 size; + unsigned int attrib; + const char *desc; + int v1only; +}; + +static struct mem_entry g_memareas[] = +{ + { 0x00010000, (16 * 1024), MEM_ATTRIB_ALL, "Scratchpad", 0 }, + { 0x40010000, (16 * 1024), MEM_ATTRIB_ALL, "Scratchpad (uncached)", 0 }, + { 0x04000000, (2 * 1024 * 1024), MEM_ATTRIB_ALL, "VRAM", 0 }, + { 0x44000000, (2 * 1024 * 1024), MEM_ATTRIB_ALL, "VRAM (uncached)", 0 }, + { 0x08800000, (24 * 1024 * 1024), MEM_ATTRIB_ALL, "User memory", 0 }, + { 0x48800000, (24 * 1024 * 1024), MEM_ATTRIB_ALL, "User memory (uncached)", 0 }, + { 0x88000000, (4 * 1024 * 1024), MEM_ATTRIB_ALL, "Kernel memory (low)", 0 }, + { 0xA8000000, (4 * 1024 * 1024), MEM_ATTRIB_ALL, "Kernel memory (low uncached)", 0 }, + /* Don't use the following 2 on a 1.5, just crashes the psp */ + { 0x88400000, (4 * 1024 * 1024), MEM_ATTRIB_ALL, "Kernel memory (mid v1.0 only)", 1 }, + { 0xA8400000, (4 * 1024 * 1024), MEM_ATTRIB_ALL, "Kernel memory (mid v1.0 only uncached)", 1 }, + { 0x88800000, (24 * 1024 * 1024), MEM_ATTRIB_ALL, "Kernel memory (high)", 0 }, + { 0xA8800000, (24 * 1024 * 1024), MEM_ATTRIB_ALL, "Kernel memory (high uncached)", 0 }, + { 0xBFC00000, (1 * 1024 * 1024), MEM_ATTRIB_ALL, "Internal RAM", 0 }, + { 0, 0, 0, NULL } +}; + +enum Operator +{ + OP_START, + OP_NONE, + OP_PLUS, + OP_MINUS, + OP_AND, + OP_OR, + OP_XOR, + OP_SHL, + OP_SHR, + OP_LAND, + OP_LOR, + OP_NEQ, + OP_EQ, + OP_LT, + OP_LEQ, + OP_GT, + OP_GEQ, + OP_MUL, + OP_DIV, +}; + +static unsigned int do_op(enum Operator op, int not, unsigned int left, unsigned int right) +{ + unsigned ret = 1; + int i; + + for(i = 0; i < not; i++) + { + right = ~right; + } + + switch(op) + { + case OP_PLUS: ret = left + right; + break; + case OP_MINUS: ret = left - right; + break; + case OP_AND: ret = left & right; + break; + case OP_OR: ret = left | right; + break; + case OP_XOR: ret = left ^ right; + break; + case OP_SHL: ret = left << right; + break; + case OP_SHR: ret = left >> right; + break; + case OP_LAND: ret = left && right; + break; + case OP_LOR: ret = left || right; + break; + case OP_NEQ: ret = left != right; + break; + case OP_EQ: ret = left == right; + break; + case OP_LT: ret = left < right; + break; + case OP_GT: ret = left > right; + break; + case OP_LEQ: ret = left <= right; + break; + case OP_GEQ: ret = left >= right; + break; + case OP_MUL: ret = left * right; + break; + case OP_DIV: if(right != 0) + { + ret = left / right; + } + else + { + SHELL_PRINT("Division by zero\n"); + } + break; + default: ret = right; + break; + }; + + return ret; +} + +static int deref_addr(unsigned int *val, int deref) +{ + int size; + int i; + + for(i = 0; i < deref; i++) + { + if(*val & 0x3) + { + SHELL_PRINT("Error, unaligned address when dereferencing\n"); + return 0; + } + + size = memValidate(*val, MEM_ATTRIB_READ | MEM_ATTRIB_WORD); + if(size < sizeof(unsigned int *)) + { + SHELL_PRINT("Error, invalid memory address when dereferencing 0x%08X\n", *val); + return 0; + } + + *val = _lw(*val); + } + + return 1; +} + +static int get_modaddr(char *name, unsigned int *val) +{ + char *pcolon; + char *pcomma; + SceKernelModuleInfo info; + SceModule *pMod; + SceUID uid = 0; + int ret = 0; + + pcolon = strchr(name, ':'); + if(pcolon) + { + *pcolon = 0; + pcolon++; + } + + pMod = sceKernelFindModuleByName(name); + if(pMod == NULL) + { + char *endp; + + uid = strtoul(name, &endp, 16); + if(*endp != 0) + { + SHELL_PRINT("Error, invalid module name %s\n", name); + return 0; + } + } + else + { + uid = pMod->modid; + } + + if(!psplinkReferModule(uid, &info)) + { + if(ret < 0) + { + SHELL_PRINT("Error, could not get module info\n"); + return 0; + } + } + + if((pcolon == NULL) || (strcmp(pcolon, "text") == 0)) + { + *val = info.text_addr; + } + else if(strcmp(pcolon, "stext") == 0) + { + *val = info.text_size; + } + else if(strcmp(pcolon, "sdata") == 0) + { + *val = info.data_size; + } + else if(strcmp(pcolon, "sbss") == 0) + { + *val = info.bss_size; + } + else if((pcomma = strchr(pcolon, ',')) != NULL) + { + /* Library export */ + *pcomma = 0; + pcomma++; + if((pcomma[0] == '0') && (pcomma[1] == 'x')) + { + unsigned int nid; + nid = strtoul(pcomma, NULL, 16); + *val = libsFindExportByNid(uid, pcolon, nid); + } + else + { + *val = libsFindExportByName(uid, pcolon, pcomma); + } + } + else if((pcolon[0] == 's') && (pcolon[1] >= '1') && (pcolon[1] <= '4') && (pcolon[2] == 0)) + { + int id = pcolon[1] - '1'; + + if(id < info.nsegment) + { + *val = info.segmentsize[id]; + } + else + { + *val = 0; + } + } + else if((pcolon[0] >= '1') && (pcolon[0] <= '4') && (pcolon[1] == 0)) + { + int id = pcolon[0] - '1'; + + if(id < info.nsegment) + { + *val = info.segmentaddr[id]; + } + else + { + *val = 0; + } + } + else + { + SHELL_PRINT("Error, invalid module address extension %s\n", pcolon); + return 0; + } + + return 1; +} + +static int get_threadaddr(char *name, unsigned int *val) +{ + char *pcolon; + SceKernelThreadInfo info; + SceUID uid; + + pcolon = strchr(name, ':'); + if(pcolon) + { + *pcolon = 0; + pcolon++; + } + + if(pspSdkReferThreadStatusByName(name, &uid, NULL)) + { + char *endp; + + uid = strtoul(name, &endp, 16); + if(*endp != 0) + { + SHELL_PRINT("Error, invalid thread name %s\n", name); + return 0; + } + } + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + if(sceKernelReferThreadStatus(uid, &info)) + { + SHELL_PRINT("Error, could not get thread info\n"); + return 0; + } + + if(pcolon == NULL) + { + *val = (unsigned int) info.entry; + } + else if(strcmp(pcolon, "stack") == 0) + { + *val = (unsigned int) info.stack; + } + else if(strcmp(pcolon, "sstack") == 0) + { + *val = info.stackSize; + } + else + { + SHELL_PRINT("Error, invalid thread address extension %s\n", pcolon); + return 0; + } + + return 1; +} + +static int parse_line(char *line, unsigned int *val) +{ + enum Operator op = OP_START; + int deref = 0; + int not = 0; + *val = 0; + + while(*line) + { + unsigned int temp; + + if(is_aspace(*line)) + { + line++; + continue; + } + else if((*line == '*') && ((op == OP_START) || (op != OP_NONE))) + { + deref++; + line++; + continue; + } + else if(*line == '~') + { + not++; + line++; + continue; + } + else if(*line == '$') + { + char buf[16]; + int pos; + unsigned int *reg; + + pos = 0; + line++; + while((pos < 15) && (is_alnum(*line))) + { + buf[pos++] = *line++; + } + buf[pos] = 0; + + reg = exceptionGetReg(buf); + if(reg == NULL) + { + SHELL_PRINT("Unknown register '%s'\n", buf); + return 0; + } + + temp = *reg; + } + else if(*line == '@') /* Module name */ + { + char *endp; + line++; + + endp = strchr(line, '@'); + if(endp == NULL) + { + SHELL_PRINT("Error, no matching '@' for module name\n"); + return 0; + } + + *endp = 0; + if(!get_modaddr(line, &temp)) + { + return 0; + } + + line = endp+1; + } + else if(*line == '%') /* Thread name */ + { + char *endp; + line++; + + endp = strchr(line, '%'); + if(endp == NULL) + { + SHELL_PRINT("Error, no matching '%%' for thread name\n"); + return 0; + } + + *endp = 0; + /* Decode the module name */ + if(!get_threadaddr(line, &temp)) + { + return 0; + } + + line = endp+1; + } + else if(*line == '(') + { + /* Scan for end of brackets, NUL terminate and pass along */ + char *pos; + int depth = 1; + + pos = ++line; + while(*pos) + { + if(*pos == '(') + { + depth++; + } + else if(*pos == ')') + { + depth--; + if(depth == 0) + { + break; + } + } + else + { + /* Do Nothing */ + } + + pos++; + } + + if(depth != 0) + { + SHELL_PRINT("Error, unmatched bracket\n"); + return 0; + } + + *pos = 0; + if(!parse_line(line, &temp)) + { + return 0; + } + line = pos + 1; + } + else if(is_hex(*line)) + { + char *endp; + + if(op == OP_NONE) + { + SHELL_PRINT("Error, cannot place two numbers together\n"); + return 0; + } + /* strtoul the value */ + temp = strtoul(line, &endp, 0); + line = endp; + } + else + { + if(op != OP_NONE) + { + SHELL_PRINT("Invalid character %c\n", *line); + return 0; + } + + /* Do switch for possible operators */ + switch(*line) + { + case '+': op = OP_PLUS; + break; + case '-': op = OP_MINUS; + break; + case '&': + if(*(line+1) == '&') + { + op = OP_LAND; + line++; + } + else + { + op = OP_AND; + } + break; + case '|': + if(*(line+1) == '|') + { + op = OP_LOR; + line++; + } + else + { + op = OP_OR; + } + break; + case '^': op = OP_XOR; + break; + case '=': + if(*(line+1) == '=') + { + op = OP_EQ; + line++; + } + else + { + SHELL_PRINT("Missing second '=' from equals operator\n"); + return 0; + } + break; + case '!': + if(*(line+1) == '=') + { + op = OP_NEQ; + line++; + } + else + { + SHELL_PRINT("Missing second '=' from not equals operator\n"); + return 0; + } + break; + case '<': if(*(line+1) == '<') + { + op = OP_SHL; + line++; + } + else if(*(line+1) == '=') + { + op = OP_LEQ; + line++; + } + else + { + op = OP_LT; + } + break; + case '>': if(*(line+1) == '>') + { + op = OP_SHR; + line++; + } + else if(*(line+1) == '=') + { + op = OP_GEQ; + line++; + } + else + { + op = OP_GT; + } + break; + case '*': op = OP_MUL; + break; + case '/': op = OP_DIV; + break; + default : SHELL_PRINT("Invalid character %c\n", *line); + return 0; + }; + line++; + continue; + } + + /* Do operation */ + if(deref > 0) + { + if(deref_addr(&temp, deref) == 0) + { + return 0; + } + } + deref = 0; + *val = do_op(op, not, *val, temp); + not = 0; + op = OP_NONE; + } + + + return 1; +} + +int memDecode(const char *line, unsigned int *val) +{ + char line_buf[1024]; + + strncpy(line_buf, line, 1023); + line_buf[1023] = 0; + + return parse_line(line_buf, val); +} + +int memValidate(unsigned int addr, unsigned int attrib) +{ + const struct mem_entry *entry; + int size_left = 0; + + entry = g_memareas; + + while(entry->size != 0) + { + if((addr >= entry->addr) && (addr < (entry->addr + (unsigned int) entry->size))) + { + /* Only pass through areas with valid attributes (e.g. write or execute) */ + if((entry->attrib & attrib) == attrib) + { + if((!entry->v1only) || (g_isv1)) + { + size_left = entry->size - (int) (addr - entry->addr); + } + } + break; + } + + entry++; + } + + if((g_protoff) && (size_left == 0)) + { + /* Allow for upto 256 byte read, so we can at least dump one page of memory in dm */ + size_left = 256; + } + + return size_left; +} + +void memPrintRegions(void) +{ + int i; + SHELL_PRINT("Memory Regions:\n"); + i = 0; + while(g_memareas[i].addr) + { + if((!g_memareas[i].v1only) || (g_isv1)) + { + SHELL_PRINT("Region %2d: Base 0x%08X - Size 0x%08X - %s\n", i, + g_memareas[i].addr, g_memareas[i].size, g_memareas[i].desc); + } + i++; + } +} + +void memSetProtoff(int protoff) +{ + g_protoff = protoff; +} diff --git a/psplink/decodeaddr.h b/psplink/decodeaddr.h new file mode 100644 index 0000000..d353c7d --- /dev/null +++ b/psplink/decodeaddr.h @@ -0,0 +1,31 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * decodeaddr.h - PSPLINK kernel module decode memory address code + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/decodeaddr.h $ + * $Id: decodeaddr.h 2301 2007-08-26 13:48:05Z tyranid $ + */ + +#ifndef __DECODEADDR_H__ +#define __DECODEADDR_H__ + +#define MEM_ATTRIB_READ (1 << 0) +#define MEM_ATTRIB_WRITE (1 << 1) +#define MEM_ATTRIB_EXEC (1 << 2) +#define MEM_ATTRIB_BYTE (1 << 3) +#define MEM_ATTRIB_HALF (1 << 4) +#define MEM_ATTRIB_WORD (1 << 5) +#define MEM_ATTRIB_DBL (1 << 6) +#define MEM_ATTRIB_ALL 0xFFFFFFFF + +int memDecode(const char *line, unsigned int *val); +int memValidate(unsigned int addr, unsigned int attrib); +void memPrintRegions(void); +void memSetProtoff(int protoff); + +#endif diff --git a/psplink/exception.c b/psplink/exception.c new file mode 100644 index 0000000..76226c3 --- /dev/null +++ b/psplink/exception.c @@ -0,0 +1,523 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * exception.c - Exception handler for psplink. + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/exception.c $ + * $Id: exception.c 2316 2007-09-17 18:13:33Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include "exception.h" +#include "util.h" +#include "psplink.h" +#include "debug.h" + +struct PsplinkContext *g_currex = NULL; +struct PsplinkContext *g_list = NULL; +struct PsplinkContext g_psplinkContext[PSPLINK_MAX_CONTEXT] __attribute__((aligned(16))); +extern struct GlobalContext g_context; + +void psplinkDefaultExHandler(void); +void psplinkDebugExHandler(void); + +#define MAT_NUM(x) ((x) / 16) +#define COL_NUM(x) (((x) / 4) & 3) +#define ROW_NUM(x) ((x) & 3) + +#define FPU_EXCEPTION 15 + +/* Mnemonic register names */ +const char *regName[32] = +{ + "zr", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" +}; + +/* Taken from the ps2, might not be 100% correct */ +static const char *codeTxt[32] = +{ + "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", "Unknown 13", "Unknown 14", + "FPU Exception", "Unknown 16", "Unknown 17", "Unknown 18", + "Unknown 20", "Unknown 21", "Unknown 22", "Unknown 23", + "Unknown 24", "Unknown 25", "Unknown 26", "Unknown 27", + "Unknown 28", "Unknown 29", "Unknown 30", "Unknown 31" +}; + +const char codeFpu[6] = { 'I', 'U', 'O', 'Z', 'V', 'E' }; +const char codeDebug[7] = { 'X', 'S', 'B', '?', '?', 'I', 'D' }; + +/* Get a pointer to a register based on its name */ +unsigned int *exceptionGetReg(const char *reg) +{ + static unsigned int modaddr = 0; + + if(g_currex == NULL) + { + return NULL; + } + + if(strcmp(reg, "epc") == 0) + { + return &g_currex->regs.epc; + } + else if(strcmp(reg, "fsr") == 0) + { + return &g_currex->regs.fsr; + } + else if(strcmp(reg, "mod") == 0) + { + SceModule *pMod; + SceKernelModuleInfo mod; + + modaddr = 0; + pMod = sceKernelFindModuleByAddress(g_currex->regs.epc); + if(pMod) + { + memset(&mod, 0, sizeof(mod)); + mod.size = sizeof(mod); + enable_kprintf(0); + if(!g_QueryModuleInfo(pMod->modid, &mod)) + { + modaddr = mod.text_addr; + } + enable_kprintf(1); + } + + return &modaddr; + } + else + { + int reg_loop; + + for(reg_loop = 0; reg_loop < 32; reg_loop++) + { + if(strcmp(regName[reg_loop], reg) == 0) + { + return &g_currex->regs.r[reg_loop]; + } + } + } + + return NULL; +} + +/* Print the cpu registers, pointer should contain a dummy entry + * for zero as it is relatively addressed */ +void exceptionPrintCPURegs(unsigned int *pRegs) +{ + int i; + + SHELL_PRINT("%s:0x00000000 %s:0x%08X %s:0x%08X %s:0x%08X\n", regName[0], + regName[1], pRegs[1], regName[2], + pRegs[2], regName[3], pRegs[3]); + for(i = 4; i < 32; i+=4) + { + SHELL_PRINT("%s:0x%08X %s:0x%08X %s:0x%08X %s:0x%08X\n", regName[i], pRegs[i], + regName[i+1], pRegs[i+1], regName[i+2], + pRegs[i+2], regName[i+3], pRegs[i+3]); + } +} + +static const char *exception_cause(struct PsplinkContext *pCtx) +{ + static char excause[40]; + + if(pCtx->regs.type == PSPLINK_EXTYPE_NORMAL) + { + if(((pCtx->regs.cause >> 2) & 31) == FPU_EXCEPTION) + { + int i; + unsigned int fpu; + char *end; + + strcpy(excause, codeTxt[(pCtx->regs.cause >> 2) & 31]); + strcat(excause, " ("); + end = excause + strlen(excause); + + fpu = pCtx->regs.fsr >> 12; + for(i = 0; i < 6; i++) + { + if((fpu >> i) & 1) + { + *end++ = codeFpu[i]; + } + } + *end++ = ')'; + *end = 0; + } + else + { + strcpy(excause, codeTxt[(pCtx->regs.cause >> 2) & 31]); + } + } + else + { + int i; + char *end; + + strcpy(excause, "DEBUG"); + strcat(excause, " ("); + end = excause + strlen(excause); + for(i = 0; i < 7; i++) + { + if(pCtx->drcntl & (1 << (i + 6))) + { + *end++ = codeDebug[i]; + } + } + *end++ = ')'; + *end = 0; + } + + return excause; +} + +/* Print the current exception */ +void exceptionPrint(struct PsplinkContext *ctx) +{ + SceModule *pMod; + SceKernelModuleInfo mod; + SceKernelThreadInfo thread; + unsigned int addr; + + if(ctx == NULL) + { + ctx = g_currex; + } + + if(ctx) + { + SHELL_PRINT("Exception - %s\n", exception_cause(ctx)); + SHELL_PRINT("Thread ID - 0x%08X\n", ctx->thid); + + memset(&thread, 0, sizeof(thread)); + thread.size = sizeof(thread); + if(!sceKernelReferThreadStatus(ctx->thid, &thread)) + { + SHELL_PRINT("Th Name - %s\n", thread.name); + } + + if((ctx->regs.epc < 0x88000000) || (ctx->regs.epc > 0x88400000)) + { + addr = ctx->regs.epc & 0x7FFFFFFF; + } + else + { + addr = ctx->regs.epc; + } + + pMod = sceKernelFindModuleByAddress(addr); + if(pMod) + { + SHELL_PRINT("Module ID - 0x%08X\n", pMod->modid); + memset(&mod, 0, sizeof(mod)); + mod.size = sizeof(mod); + enable_kprintf(0); + if(!g_QueryModuleInfo(pMod->modid, &mod)) + { + SHELL_PRINT("Mod Name - %s\n", mod.name); + } + enable_kprintf(1); + } + + SHELL_PRINT("EPC - 0x%08X\n", ctx->regs.epc); + if(g_currex->regs.type == PSPLINK_EXTYPE_NORMAL) + { + SHELL_PRINT("Cause - 0x%08X\n", ctx->regs.cause); + SHELL_PRINT("BadVAddr - 0x%08X\n", ctx->regs.badvaddr); + } + else + { + SHELL_PRINT("DRCNTL - 0x%08X\n", ctx->drcntl); + } + + SHELL_PRINT("Status - 0x%08X\n", ctx->regs.status); + exceptionPrintCPURegs(ctx->regs.r); + } + else + { + SHELL_PRINT("No exception occurred\n"); + } +} + +void exceptionList(void) +{ + int i; + + if(g_list) + { + for(i = 0; i < PSPLINK_MAX_CONTEXT; i++) + { + if(g_list[i].valid) + { + SHELL_PRINT("Exception %-2d: EPC 0x%08X, Cause %s\n", i, g_list[i].regs.epc, + exception_cause(&g_list[i])); + } + } + } + else + { + SHELL_PRINT("No exception handler registered\n"); + } +} + +void exceptionPrintFPURegs(float *pFpu, unsigned int fsr, unsigned int fir) +{ + int i; + + pspSdkDisableFPUExceptions(); + + for(i = 0; i < 32; i+=2) + { + char left[64], right[64]; + + f_cvt(&pFpu[i], left, sizeof(left), 6, MODE_GENERIC); + f_cvt(&pFpu[i+1], right, sizeof(right), 6, MODE_GENERIC); + SHELL_PRINT("fpr%02d: %-20s - fpr%02d: %-20s\n", i, left, i+1, right); + } + SHELL_PRINT("fsr: %08X - fir %08X\n", fsr, fir); +} + +void exceptionFpuPrint(struct PsplinkContext *ctx) +{ + if(ctx == NULL) + { + ctx = g_currex; + } + + if(ctx) + { + if(ctx->regs.status & 0x20000000) + { + exceptionPrintFPURegs(ctx->regs.fpr, ctx->regs.fsr, + ctx->regs.fir); + } + else + { + SHELL_PRINT("FPU not enabled in context\n"); + } + } + else + { + SHELL_PRINT("No exception occurred\n"); + } +} + +static void print_vfpu_row(char type, int m, int c, int r, float x, float y, float z, float w) +{ + char xs[64], ys[64], zs[64], ws[64]; + + f_cvt(&x, xs, sizeof(xs), 6, MODE_GENERIC); + f_cvt(&y, ys, sizeof(ys), 6, MODE_GENERIC); + f_cvt(&z, zs, sizeof(zs), 6, MODE_GENERIC); + f_cvt(&w, ws, sizeof(ws), 6, MODE_GENERIC); + if(type != 0) + { + SHELL_PRINT("%c%d%d%d: { %14s, %14s, %14s, %14s }\n", + type, m, c, r, xs, ys, zs, ws ); + } + else + { + SHELL_PRINT(" { %14s, %14s, %14s, %14s }\n", + xs, ys, zs, ws ); + } +} + +void exceptionPrintVFPURegs(float *pFpu, int mode) +{ + int i, j; + + pspSdkDisableFPUExceptions(); + + if(mode == VFPU_PRINT_SINGLE) + { + for(i = 0; i < 128; i+=2) + { + char left[64], right[64]; + + f_cvt(&pFpu[i], left, sizeof(left), 6, MODE_GENERIC); + f_cvt(&pFpu[i+1], right, sizeof(right), 6, MODE_GENERIC); + SHELL_PRINT("S%d%d%d: %-20s - S%d%d%d: %-20s\n", + MAT_NUM(i), COL_NUM(i), ROW_NUM(i), left, + MAT_NUM(i+1), COL_NUM(i+1), ROW_NUM(i+1), right); + } + } + else if(mode == VFPU_PRINT_COL) + { + for(i = 0; i < 128; i+=4) + { + print_vfpu_row('C', MAT_NUM(i), COL_NUM(i), 0, pFpu[i], pFpu[i+1], pFpu[i+2], pFpu[i+3]); + } + } + else if(mode == VFPU_PRINT_ROW) + { + for(i = 0; i < 128; i+=16) + { + for(j = 0; j < 4; j++) + { + print_vfpu_row('R', MAT_NUM(i), 0, j, pFpu[i+j], pFpu[i+j+4], pFpu[i+j+8], pFpu[i+j+12]); + } + } + } + else if(mode == VFPU_PRINT_MATRIX) + { + for(i = 0; i < 128; i+=16) + { + print_vfpu_row('M', MAT_NUM(i), 0, 0, pFpu[i], pFpu[i+4], pFpu[i+8], pFpu[i+12]); + for(j = 1; j < 4; j++) + { + print_vfpu_row(0, 0, 0, 0, pFpu[i+j], pFpu[i+j+4], pFpu[i+j+8], pFpu[i+j+12]); + } + } + } + else if(mode == VFPU_PRINT_TRANS) + { + for(i = 0; i < 128; i+=16) + { + print_vfpu_row('E', MAT_NUM(i), 0, 0, pFpu[i], pFpu[i+1], pFpu[i+2], pFpu[i+3]); + for(j = 1; j < 4; j++) + { + print_vfpu_row(0, 0, 0, 0, pFpu[i+(j*4)], pFpu[i+(j*4)+1], pFpu[i+(j*4)+2], pFpu[i+(j*4)+3]); + } + } + } +} + +void exceptionVfpuPrint(struct PsplinkContext *ctx, int mode) +{ + if(ctx == NULL) + { + ctx = g_currex; + } + + if(ctx) + { + if(ctx->regs.status & 0x40000000) + { + exceptionPrintVFPURegs(ctx->regs.vfpu, mode); + } + else + { + SHELL_PRINT("VFPU not enabled in context\n"); + } + } + else + { + SHELL_PRINT("No exception occurred\n"); + } +} + +struct PsplinkContext* psplinkTrap(struct PsplinkContext *ctx) +{ + int skip = 0; + + /* Only set if we are not already messing with a context */ + if(g_currex == NULL) + { + g_currex = ctx; + } + + if(sceKernelIsIntrContext()) + { + Kprintf("Can't debug interrupt contexts (ATM)\n"); + for(;;) { ; } + } + else + { + ctx->thid = sceKernelGetThreadId(); + } + /* Handle debug case, check whether we are in an interrupt context etc. */ + /* Also disable the current breakpoint if we hit it */ + + debugClearException(ctx); + + /* Enable interrupts now */ + asm __volatile__ ( + "mfc0 $v0, $12\n" + "ori $v0, $v0, 1\n" + "mtc0 $v0, $12\n" + "nop\n" + ); + + debugHandleException(ctx); + + if(ctx->cont == PSP_EXCEPTION_EXIT) + { + sceKernelExitDeleteThread(0); + } + + /* Disable interrupts now */ + asm __volatile__ ( + "mtic $0, $0\n" + "nop\n" + "nop\n" + ); + + switch(ctx->cont) + { + case PSP_EXCEPTION_SKIP: skip = 1; + case PSP_EXCEPTION_STEP: debugStep(ctx, skip); + break; + default: break; + }; + + if(g_currex == ctx) + { + g_currex = NULL; + } + + return ctx; +} + +void exceptionResume(struct PsplinkContext *ctx, int cont) +{ + if(ctx == NULL) + { + ctx = g_currex; + } + + if(ctx) + { + ctx->cont = cont; + sceKernelWakeupThread(ctx->thid); + } +} + +void exceptionSetCtx(int ex) +{ + if((ex >= 0) && (ex < PSPLINK_MAX_CONTEXT)) + { + if((g_list) && (g_list[ex].valid)) + { + g_currex = &g_list[ex]; + } + } +} + +void exceptionInit(void) +{ + int i; + memset(g_psplinkContext, 0, sizeof(g_psplinkContext)); + + for(i = 0; i < (PSPLINK_MAX_CONTEXT-1); i++) + { + g_psplinkContext[i].pNext = &g_psplinkContext[i+1]; + } + + g_list = g_psplinkContext; + sceKernelRegisterDefaultExceptionHandler((void*) psplinkDefaultExHandler); +} diff --git a/psplink/exception.h b/psplink/exception.h new file mode 100644 index 0000000..1a393d7 --- /dev/null +++ b/psplink/exception.h @@ -0,0 +1,96 @@ +#ifndef __EXCEPTION_H__ +#define __EXCEPTION_H__ + +#include + +/* Define maximum number of thread exception context */ +#define PSPLINK_MAX_CONTEXT 8 + +#define PSPLINK_EXTYPE_NORMAL 0 +#define PSPLINK_EXTYPE_DEBUG 1 + +/* Define continue flags */ +#define PSP_EXCEPTION_EXIT 0 +#define PSP_EXCEPTION_CONTINUE 1 +#define PSP_EXCEPTION_STEP 2 +#define PSP_EXCEPTION_SKIP 3 +#define PSP_EXCEPTION_NOT_HANDLED 4 + +/** Structure to hold the register data associated with an exception */ +typedef struct _PsplinkRegBlock +{ + unsigned int frame[6]; + /** Array of the 32 GPRs */ + unsigned int r[32]; + /** The status register */ + unsigned int status; + /** lo */ + unsigned int lo; + unsigned int hi; + unsigned int badvaddr; + unsigned int cause; + unsigned int epc; + float fpr[32]; + unsigned int fsr; + unsigned int fir; + unsigned int frame_ptr; + unsigned int unused; + /* Unused on PSP */ + unsigned int index; + unsigned int random; + unsigned int entrylo0; + unsigned int entrylo1; + unsigned int context; + unsigned int pagemask; + unsigned int wired; + unsigned int cop0_7; + unsigned int cop0_8; + unsigned int cop0_9; + unsigned int entryhi; + unsigned int cop0_11; + unsigned int cop0_12; + unsigned int cop0_13; + unsigned int cop0_14; + /* PRId should still be okay */ + unsigned int prid; + /* Type of exception (normal or debug) */ + unsigned int type; + /* Pad vfpu to 128bit boundary */ + int pad; + float vfpu[128]; +} PsplinkRegBlock; + +/* A thread context during an exception */ +struct PsplinkContext +{ + int valid; + struct PsplinkContext *pNext; + PsplinkRegBlock regs; + SceUID thid; + unsigned int drcntl; + /* Continue type */ + int cont; + /* Indicates whether this was an error or planned */ + int error; +}; + +#define VFPU_PRINT_SINGLE 0 +#define VFPU_PRINT_COL 1 +#define VFPU_PRINT_ROW 2 +#define VFPU_PRINT_MATRIX 3 +#define VFPU_PRINT_TRANS 4 + +extern struct PsplinkContext *g_currex; + +void exceptionInit(void); +void exceptionPrint(struct PsplinkContext *ctx); +void exceptionFpuPrint(struct PsplinkContext *ctx); +void exceptionVfpuPrint(struct PsplinkContext *ctx, int mode); +unsigned int *exceptionGetReg(const char *reg); +void exceptionResume(struct PsplinkContext *ctx, int cont); +void exceptionPrintFPURegs(float *pFpu, unsigned int fsr, unsigned int fir); +void exceptionPrintCPURegs(unsigned int *pRegs); +void exceptionList(void); +void exceptionSetCtx(int ex); + +#endif diff --git a/psplink/exception_asm.S b/psplink/exception_asm.S new file mode 100644 index 0000000..4a1ebe3 --- /dev/null +++ b/psplink/exception_asm.S @@ -0,0 +1,633 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * exception_asm.S - Asm code for PSPLINK user exceptions + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/psplink_user/exception_asm.S $ + * $Id: exception_asm.S 1913 2006-05-21 17:26:57Z tyranid $ + */ + + .set noreorder + .set noat + +#define BadVAddr $8 // Address for the most recent address-related exception +#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 FSR $31 +#define FIR $0 + +#define REG_GPR_0 (6*4) +#define REG_GPR_1 (REG_GPR_0 + 4) +#define REG_GPR_2 (REG_GPR_1 + 4) +#define REG_GPR_3 (REG_GPR_2 + 4) +#define REG_GPR_4 (REG_GPR_3 + 4) +#define REG_GPR_5 (REG_GPR_4 + 4) +#define REG_GPR_6 (REG_GPR_5 + 4) +#define REG_GPR_7 (REG_GPR_6 + 4) +#define REG_GPR_8 (REG_GPR_7 + 4) +#define REG_GPR_9 (REG_GPR_8 + 4) +#define REG_GPR_10 (REG_GPR_9 + 4) +#define REG_GPR_11 (REG_GPR_10 + 4) +#define REG_GPR_12 (REG_GPR_11 + 4) +#define REG_GPR_13 (REG_GPR_12 + 4) +#define REG_GPR_14 (REG_GPR_13 + 4) +#define REG_GPR_15 (REG_GPR_14 + 4) +#define REG_GPR_16 (REG_GPR_15 + 4) +#define REG_GPR_17 (REG_GPR_16 + 4) +#define REG_GPR_18 (REG_GPR_17 + 4) +#define REG_GPR_19 (REG_GPR_18 + 4) +#define REG_GPR_20 (REG_GPR_19 + 4) +#define REG_GPR_21 (REG_GPR_20 + 4) +#define REG_GPR_22 (REG_GPR_21 + 4) +#define REG_GPR_23 (REG_GPR_22 + 4) +#define REG_GPR_24 (REG_GPR_23 + 4) +#define REG_GPR_25 (REG_GPR_24 + 4) +#define REG_GPR_26 (REG_GPR_25 + 4) +#define REG_GPR_27 (REG_GPR_26 + 4) +#define REG_GPR_28 (REG_GPR_27 + 4) +#define REG_GPR_29 (REG_GPR_28 + 4) +#define REG_GPR_30 (REG_GPR_29 + 4) +#define REG_GPR_31 (REG_GPR_30 + 4) + +#define REG_STATUS (REG_GPR_31 + 4) +#define REG_LO (REG_STATUS + 4) +#define REG_HI (REG_LO + 4) +#define REG_BADVADDR (REG_HI + 4) +#define REG_CAUSE (REG_BADVADDR + 4) +#define REG_EPC (REG_CAUSE + 4) + +#define REG_FPR_0 (REG_EPC + 4) +#define REG_FPR_1 (REG_FPR_0 + 4) +#define REG_FPR_2 (REG_FPR_1 + 4) +#define REG_FPR_3 (REG_FPR_2 + 4) +#define REG_FPR_4 (REG_FPR_3 + 4) +#define REG_FPR_5 (REG_FPR_4 + 4) +#define REG_FPR_6 (REG_FPR_5 + 4) +#define REG_FPR_7 (REG_FPR_6 + 4) +#define REG_FPR_8 (REG_FPR_7 + 4) +#define REG_FPR_9 (REG_FPR_8 + 4) +#define REG_FPR_10 (REG_FPR_9 + 4) +#define REG_FPR_11 (REG_FPR_10 + 4) +#define REG_FPR_12 (REG_FPR_11 + 4) +#define REG_FPR_13 (REG_FPR_12 + 4) +#define REG_FPR_14 (REG_FPR_13 + 4) +#define REG_FPR_15 (REG_FPR_14 + 4) +#define REG_FPR_16 (REG_FPR_15 + 4) +#define REG_FPR_17 (REG_FPR_16 + 4) +#define REG_FPR_18 (REG_FPR_17 + 4) +#define REG_FPR_19 (REG_FPR_18 + 4) +#define REG_FPR_20 (REG_FPR_19 + 4) +#define REG_FPR_21 (REG_FPR_20 + 4) +#define REG_FPR_22 (REG_FPR_21 + 4) +#define REG_FPR_23 (REG_FPR_22 + 4) +#define REG_FPR_24 (REG_FPR_23 + 4) +#define REG_FPR_25 (REG_FPR_24 + 4) +#define REG_FPR_26 (REG_FPR_25 + 4) +#define REG_FPR_27 (REG_FPR_26 + 4) +#define REG_FPR_28 (REG_FPR_27 + 4) +#define REG_FPR_29 (REG_FPR_28 + 4) +#define REG_FPR_30 (REG_FPR_29 + 4) +#define REG_FPR_31 (REG_FPR_30 + 4) + +#define REG_FSR (REG_FPR_31 + 4) +#define REG_FIR (REG_FSR + 4) +#define REG_FP (REG_FIR + 4) +#define REG_UNUSED1 (REG_FP + 4) +#define REG_INDEX (REG_UNUSED1 + 4) +#define REG_RANDOM (REG_INDEX + 4) +#define REG_ENTRYLO0 (REG_RANDOM + 4) +#define REG_ENTRYLO1 (REG_ENTRYLO0 + 4) +#define REG_CONTEXT (REG_ENTRYLO1 + 4) +#define REG_PAGEMASK (REG_CONTEXT + 4) +#define REG_WIRED (REG_PAGEMASK + 4) +#define REG_COP0_7 (REG_WIRED + 4) +#define REG_COP0_8 (REG_COP0_7 + 4) +#define REG_COP0_9 (REG_COP0_8 + 4) +#define REG_ENTRYHI (REG_COP0_9 + 4) +#define REG_COP0_11 (REG_ENTRYHI + 4) +#define REG_COP0_12 (REG_COP0_11 + 4) +#define REG_COP0_13 (REG_COP0_12 + 4) +#define REG_COP0_14 (REG_COP0_13 + 4) +#define REG_PRID (REG_COP0_14 + 4) +#define REG_TYPE (REG_PRID + 4) +#define REG_VFPU (REG_TYPE + 8) + + .extern g_psplinkContext + .extern psplinkTrap + + .global psplinkDefaultExHandler + .ent psplinkDefaultExHandler +psplinkDefaultExHandler: + + nop + nop +# Set exception type to NORMAL + la $v1, _psplinkExType + sw $0, 0($v1) + j psplinkExceptionHandler + nop + + .end psplinkDefaultExHandler + + .global psplinkDebugExHandler + .ent psplinkDebugExHandler +psplinkDebugExHandler: +# Set exception type to DEBUG + la $v1, _psplinkExType + li $v0, 1 + sw $v0, 0($v1) + j psplinkExceptionHandler + nop + .end psplinkDebugExHandler + + .global psplinkExceptionHandler + .ent psplinkExceptionHandler +psplinkExceptionHandler: + +# Scan the context array for a free handler, if we don't find one pass +# NULL in $a0 so the thread can be terminated + la $v0, g_psplinkContext +1: + # If we have no more context then exit + beq $v0, $0, 4f + nop + + lw $v1, 0($v0) + bnel $v1, $0, 1b + lw $v0, 4($v0) + + addiu $v1, $0, 1 + sw $v1, 0($v0) + addiu $v0, $v0, 8 + + sw $0, REG_GPR_0($v0) + sw $1, REG_GPR_1($v0) + + cfc0 $1, $4 # Get original v0 + sw $1, REG_GPR_2($v0) + cfc0 $1, $5 # Get original v1 + sw $1, REG_GPR_3($v0) + sw $4, REG_GPR_4($v0) + sw $5, REG_GPR_5($v0) + sw $6, REG_GPR_6($v0) + sw $7, REG_GPR_7($v0) + sw $8, REG_GPR_8($v0) + sw $9, REG_GPR_9($v0) + sw $10, REG_GPR_10($v0) + sw $11, REG_GPR_11($v0) + sw $12, REG_GPR_12($v0) + sw $13, REG_GPR_13($v0) + sw $14, REG_GPR_14($v0) + sw $15, REG_GPR_15($v0) + sw $16, REG_GPR_16($v0) + sw $17, REG_GPR_17($v0) + sw $18, REG_GPR_18($v0) + sw $19, REG_GPR_19($v0) + sw $20, REG_GPR_20($v0) + sw $21, REG_GPR_21($v0) + sw $22, REG_GPR_22($v0) + sw $23, REG_GPR_23($v0) + sw $24, REG_GPR_24($v0) + sw $25, REG_GPR_25($v0) + sw $26, REG_GPR_26($v0) + sw $27, REG_GPR_27($v0) + sw $28, REG_GPR_28($v0) + sw $29, REG_GPR_29($v0) + sw $30, REG_GPR_30($v0) + sw $31, REG_GPR_31($v0) + + mflo $v1 + sw $v1, REG_LO($v0) + mfhi $v1 + sw $v1, REG_HI($v0) + mfc0 $v1, BadVAddr + sw $v1, REG_BADVADDR($v0) +# Get cause + cfc0 $v1, $3 + sw $v1, REG_CAUSE($v0) +# Get EPC + cfc0 $v1, $0 + sw $v1, REG_EPC($v0) +# Get Status + cfc0 $v1, $2 + sw $v1, REG_STATUS($v0) + +# Copy in the exception type + la $a0, _psplinkExType + lw $a0, 0($a0) + sw $a0, REG_TYPE($v0) + +# Check if cop1 is enabled and skip if not + lui $a0, 0x2000 + and $a0, $a0, $v1 + beq $a0, $0, 2f + nop + + swc1 $0, REG_FPR_0($v0) + swc1 $1, REG_FPR_1($v0) + swc1 $2, REG_FPR_2($v0) + swc1 $3, REG_FPR_3($v0) + swc1 $4, REG_FPR_4($v0) + swc1 $5, REG_FPR_5($v0) + swc1 $6, REG_FPR_6($v0) + swc1 $7, REG_FPR_7($v0) + swc1 $8, REG_FPR_8($v0) + swc1 $9, REG_FPR_9($v0) + swc1 $10, REG_FPR_10($v0) + swc1 $11, REG_FPR_11($v0) + swc1 $12, REG_FPR_12($v0) + swc1 $13, REG_FPR_13($v0) + swc1 $14, REG_FPR_14($v0) + swc1 $15, REG_FPR_15($v0) + swc1 $16, REG_FPR_16($v0) + swc1 $17, REG_FPR_17($v0) + swc1 $18, REG_FPR_18($v0) + swc1 $19, REG_FPR_19($v0) + swc1 $20, REG_FPR_20($v0) + swc1 $21, REG_FPR_21($v0) + swc1 $22, REG_FPR_22($v0) + swc1 $23, REG_FPR_23($v0) + swc1 $24, REG_FPR_24($v0) + swc1 $25, REG_FPR_25($v0) + swc1 $26, REG_FPR_26($v0) + swc1 $27, REG_FPR_27($v0) + swc1 $28, REG_FPR_28($v0) + swc1 $29, REG_FPR_29($v0) + swc1 $30, REG_FPR_30($v0) + swc1 $31, REG_FPR_31($v0) + + cfc1 $t0, FSR + sw $t0, REG_FSR($v0) + cfc1 $t0, FIR + sw $t0, REG_FIR($v0) + ctc1 $0, FSR # Clear any cause flags + +# Jump target for ignore cop1 +2: + +# Check for VFPU enable + cfc0 $v1, $2 + lui $a0, 0x4000 + and $a0, $a0, $v1 + beq $a0, $0, 3f + nop + +# Store VFPU registers (memory should be aligned) + sv.q C000, REG_VFPU($v0) + sv.q C010, REG_VFPU+16($v0) + sv.q C020, REG_VFPU+32($v0) + sv.q C030, REG_VFPU+48($v0) + sv.q C100, REG_VFPU+64($v0) + sv.q C110, REG_VFPU+80($v0) + sv.q C120, REG_VFPU+96($v0) + sv.q C130, REG_VFPU+112($v0) + sv.q C200, REG_VFPU+128($v0) + sv.q C210, REG_VFPU+144($v0) + sv.q C220, REG_VFPU+160($v0) + sv.q C230, REG_VFPU+176($v0) + sv.q C300, REG_VFPU+192($v0) + sv.q C310, REG_VFPU+208($v0) + sv.q C320, REG_VFPU+224($v0) + sv.q C330, REG_VFPU+240($v0) + sv.q C400, REG_VFPU+256($v0) + sv.q C410, REG_VFPU+272($v0) + sv.q C420, REG_VFPU+288($v0) + sv.q C430, REG_VFPU+304($v0) + sv.q C500, REG_VFPU+320($v0) + sv.q C510, REG_VFPU+336($v0) + sv.q C520, REG_VFPU+352($v0) + sv.q C530, REG_VFPU+368($v0) + sv.q C600, REG_VFPU+384($v0) + sv.q C610, REG_VFPU+400($v0) + sv.q C620, REG_VFPU+416($v0) + sv.q C630, REG_VFPU+432($v0) + sv.q C700, REG_VFPU+448($v0) + sv.q C710, REG_VFPU+464($v0) + sv.q C720, REG_VFPU+480($v0) + sv.q C730, REG_VFPU+496($v0) + +# Jump target for ignore cop2 +3: + sw $sp, REG_FP($v0) + addiu $v0, $v0, -8 + +4: +# Transition this thread into kernel mode if necessary +# Get status reg and SP.user + cfc0 $t0, $2 + cfc0 $t3, $15 + andi $t2, $t0, 0x18 + +# If coming from kernel mode then leave stack as is + movz $t3, $sp, $t2 + +# Create bodged SCCONTEXT, doesn't really matter too much as +# We will restore the regs correctly anyway ;) + addiu $t3, $t3, -32 + + sw $t0, 0($t3) + cfc0 $t0, $0 + sw $t0, 4($t3) + sw $sp, 8($t3) + sw $ra, 0xC($t3) + sw $k1, 0x10($t3) + sw $zero, 0x14($t3) + sw $zero, 0x18($t3) + sw $zero, 0x1C($t3) + +# Setup TCB + cfc0 $t0, $16 + beqz $t0, 5f + move $sp, $t3 + + lw $t1, 0($t0) + sw $t1, 0x1C($sp) + sw $sp, 0($t0) + +5: + cfc0 $t0, $2 +# Don't enable interrupts yet + li $t1, 0xFFFFFFE4 + and $t0, $t0, $t1 + nop + nop + mtc0 $t0, $12 + nop + +# Assume we always came from kernel mode to placate the kernel + move $k1, $0 + +# Jump to entry + jal psplinkTrap + move $a0, $v0 + +# psplinkTrap returns the context block to restart +# Restore context + sw $0, 0($v0) + addiu $v0, $v0, 8 +# Restore exception mode and restore registers + lw $v1, REG_STATUS($v0) + mtc0 $v1, $12 + nop + +# Restore TCB + cfc0 $t0, $16 + beqz $t0, 6f + nop + + lw $t1, 0x1C($sp) + sw $t1, 0($t0) +6: + +# Check if cop2 is enabled and skip if not + lui $a0, 0x4000 + and $a0, $a0, $v1 + beq $a0, $0, 1f + nop + +# Restore VFPU registers + lv.q C000, REG_VFPU($v0) + lv.q C010, REG_VFPU+16($v0) + lv.q C020, REG_VFPU+32($v0) + lv.q C030, REG_VFPU+48($v0) + lv.q C100, REG_VFPU+64($v0) + lv.q C110, REG_VFPU+80($v0) + lv.q C120, REG_VFPU+96($v0) + lv.q C130, REG_VFPU+112($v0) + lv.q C200, REG_VFPU+128($v0) + lv.q C210, REG_VFPU+144($v0) + lv.q C220, REG_VFPU+160($v0) + lv.q C230, REG_VFPU+176($v0) + lv.q C300, REG_VFPU+192($v0) + lv.q C310, REG_VFPU+208($v0) + lv.q C320, REG_VFPU+224($v0) + lv.q C330, REG_VFPU+240($v0) + lv.q C400, REG_VFPU+256($v0) + lv.q C410, REG_VFPU+272($v0) + lv.q C420, REG_VFPU+288($v0) + lv.q C430, REG_VFPU+304($v0) + lv.q C500, REG_VFPU+320($v0) + lv.q C510, REG_VFPU+336($v0) + lv.q C520, REG_VFPU+352($v0) + lv.q C530, REG_VFPU+368($v0) + lv.q C600, REG_VFPU+384($v0) + lv.q C610, REG_VFPU+400($v0) + lv.q C620, REG_VFPU+416($v0) + lv.q C630, REG_VFPU+432($v0) + lv.q C700, REG_VFPU+448($v0) + lv.q C710, REG_VFPU+464($v0) + lv.q C720, REG_VFPU+480($v0) + lv.q C730, REG_VFPU+496($v0) + +1: + + lw $v1, REG_STATUS($v0) + +# Check if cop1 is enabled and skip if not + lui $a0, 0x2000 + and $a0, $a0, $v1 + beq $a0, $0, 2f + nop + + lwc1 $0, REG_FPR_0($v0) + lwc1 $1, REG_FPR_1($v0) + lwc1 $2, REG_FPR_2($v0) + lwc1 $3, REG_FPR_3($v0) + lwc1 $4, REG_FPR_4($v0) + lwc1 $5, REG_FPR_5($v0) + lwc1 $6, REG_FPR_6($v0) + lwc1 $7, REG_FPR_7($v0) + lwc1 $8, REG_FPR_8($v0) + lwc1 $9, REG_FPR_9($v0) + lwc1 $10, REG_FPR_10($v0) + lwc1 $11, REG_FPR_11($v0) + lwc1 $12, REG_FPR_12($v0) + lwc1 $13, REG_FPR_13($v0) + lwc1 $14, REG_FPR_14($v0) + lwc1 $15, REG_FPR_15($v0) + lwc1 $16, REG_FPR_16($v0) + lwc1 $17, REG_FPR_17($v0) + lwc1 $18, REG_FPR_18($v0) + lwc1 $19, REG_FPR_19($v0) + lwc1 $20, REG_FPR_20($v0) + lwc1 $21, REG_FPR_21($v0) + lwc1 $22, REG_FPR_22($v0) + lwc1 $23, REG_FPR_23($v0) + lwc1 $24, REG_FPR_24($v0) + lwc1 $25, REG_FPR_25($v0) + lwc1 $26, REG_FPR_26($v0) + lwc1 $27, REG_FPR_27($v0) + lwc1 $28, REG_FPR_28($v0) + lwc1 $29, REG_FPR_29($v0) + lwc1 $30, REG_FPR_30($v0) + lwc1 $31, REG_FPR_31($v0) + + lw $t0, REG_FSR($v0) + li $t1, 0xFFFC0F83 + and $t0, $t0, $t1 # Clear the cause and flags before ret + ctc1 $t0, FSR +2: + +# Don't do 1 or 2 yet + lw $3, REG_GPR_3($v0) + lw $4, REG_GPR_4($v0) + lw $5, REG_GPR_5($v0) + lw $6, REG_GPR_6($v0) + lw $7, REG_GPR_7($v0) + lw $8, REG_GPR_8($v0) + lw $9, REG_GPR_9($v0) + lw $10, REG_GPR_10($v0) + lw $11, REG_GPR_11($v0) + lw $12, REG_GPR_12($v0) + lw $13, REG_GPR_13($v0) + lw $14, REG_GPR_14($v0) + lw $15, REG_GPR_15($v0) + lw $16, REG_GPR_16($v0) + lw $17, REG_GPR_17($v0) + lw $18, REG_GPR_18($v0) + lw $19, REG_GPR_19($v0) + lw $20, REG_GPR_20($v0) + lw $21, REG_GPR_21($v0) + lw $22, REG_GPR_22($v0) + lw $23, REG_GPR_23($v0) + lw $24, REG_GPR_24($v0) + lw $25, REG_GPR_25($v0) + lw $26, REG_GPR_26($v0) + lw $27, REG_GPR_27($v0) + lw $28, REG_GPR_28($v0) + lw $29, REG_GPR_29($v0) + lw $30, REG_GPR_30($v0) + lw $31, REG_GPR_31($v0) + + lw $1, REG_EPC($v0) + mtc0 $1, EPC + +# Restore 1 + 2 now + lw $1, REG_GPR_1($v0) + lw $2, REG_GPR_2($v0) + + nop + nop + eret + + .end psplinkExceptionHandler + + .global psplinkHwDebugTrap + .global psplinkHwDebugTrapEnd + .ent psplinkHwDebugTrap +psplinkHwDebugTrap: + ctc0 $v0, $26 + cfc0 $v0, $10 + jr $v0 + nop +psplinkHwDebugTrapEnd: + .end psplinkHwDebugTrap + + .global psplinkHwDebugMain + .ent psplinkHwDebugMain +psplinkHwDebugMain: + ctc0 $v1, $27 + la $v1, psplinkHwContext + lw $v0, 0($v1) + andi $v0, $v0, 1 + bnez $v0, get_context + nop + la $v1, psplinkHwContext + lw $v0, 0($v1) + andi $v0, $v0, 2 + bnez $v0, set_context + nop + + mfdr $v0, $0 + li $v1, 0xFFFFFFDF + and $v0, $v0, $v1 + la $v1, psplinkHwContext + sw $v0, 4($v1) + mtdr $v0, $0 + + cfc0 $v0, $26 + cfc0 $v1, $27 + ctc0 $v0, $4 + ctc0 $v1, $5 + mfdr $v0, $1 + ctc0 $v0, $0 + la $v1, psplinkDebugExHandler + mtdr $v1, $1 + mfc0 $v0, Cause + ctc0 $v0, $3 + mfc0 $v0, Status + li $v1, 2 + or $v0, $v0, $v1 + ctc0 $v0, $2 + mtc0 $v0, Status + dret + nop + +get_context: + mfdr $v0, $1 + addiu $v0, $v0, 4 + mtdr $v0, $1 + sw $zero, 0($v1) + mfdr $v0, $0 + sw $v0, 4($v1) + mfdr $v0, $4 + sw $v0, 8($v1) + mfdr $v0, $5 + sw $v0, 0xC($v1) + mfdr $v0, $8 + sw $v0, 0x10($v1) + mfdr $v0, $9 + sw $v0, 0x14($v1) + mfdr $v0, $12 + sw $v0, 0x18($v1) + mfdr $v0, $13 + sw $v0, 0x1C($v1) + mfdr $v0, $14 + sw $v0, 0x20($v1) + mfdr $v0, $15 + sw $v0, 0x24($v1) + cfc0 $v0, $26 + cfc0 $v1, $27 + dret + nop + +set_context: + mfdr $v0, $1 + addiu $v0, $v0, 4 + mtdr $v0, $1 + + lw $v0, 8($v1) + mtdr $v0, $4 + lw $v0, 0xC($v1) + mtdr $v0, $5 + lw $v0, 0x10($v1) + mtdr $v0, $8 + lw $v0, 0x14($v1) + mtdr $v0, $9 + lw $v0, 0x18($v1) + mtdr $v0, $12 + lw $v0, 0x1C($v1) + mtdr $v0, $13 + lw $v0, 0x20($v1) + mtdr $v0, $14 + lw $v0, 0x24($v1) + mtdr $v0, $15 + cfc0 $v0, $26 + cfc0 $v1, $27 + dret + nop + + .end psplinkHwDebugMain + + .data + + .global _psplinkExType +_psplinkExType: + .word 0 diff --git a/psplink/exports.exp b/psplink/exports.exp new file mode 100644 index 0000000..d52af65 --- /dev/null +++ b/psplink/exports.exp @@ -0,0 +1,45 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_EXPORT_START(psplink_driver, 0, 0x0001) +PSP_EXPORT_FUNC(psplinkPresent) +PSP_EXPORT_FUNC(psplinkExitShell) +PSP_EXPORT_FUNC(psplinkReferModule) +PSP_EXPORT_FUNC(psplinkGetFullThreadContext) +PSP_EXPORT_FUNC(psplinkReferModuleByName) +PSP_EXPORT_FUNC(psplinkReferThreadsByModule) +PSP_EXPORT_FUNC(psplinkReset) +PSP_EXPORT_FUNC(apiHookByName) +PSP_EXPORT_FUNC(apiHookByNid) +PSP_EXPORT_FUNC(debugRegisterEventHandler) +PSP_EXPORT_FUNC(debugUnregisterEventHandler) +PSP_EXPORT_FUNC(debugWaitDebugEvent) +PSP_EXPORT_FUNC(debugDeleteBP) +PSP_EXPORT_FUNC(debugDisableBP) +PSP_EXPORT_FUNC(debugEnableBP) +PSP_EXPORT_FUNC(debugFindBPByIndex) +PSP_EXPORT_FUNC(debugSetBP) +PSP_EXPORT_FUNC(debugBreakThread) +PSP_EXPORT_END + +PSP_EXPORT_START(psplink, 0, 0x4001) +PSP_EXPORT_FUNC(psplinkPresent) +PSP_EXPORT_FUNC(psplinkExitShell) +PSP_EXPORT_FUNC(psplinkReferModule) +PSP_EXPORT_FUNC(psplinkReferModuleByName) +PSP_EXPORT_FUNC(psplinkReset) +PSP_EXPORT_FUNC(apiHookByName) +PSP_EXPORT_FUNC(apiHookByNid) +PSP_EXPORT_FUNC(_apiHookHandle) +PSP_EXPORT_FUNC(_apiHookReturn) +PSP_EXPORT_FUNC(apiHookRegisterUserDispatch) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/psplink/kmode.S b/psplink/kmode.S new file mode 100644 index 0000000..99a9962 --- /dev/null +++ b/psplink/kmode.S @@ -0,0 +1,309 @@ + + .set noreorder + .set noat + + .global psplinkSetK1 + .ent psplinkSetK1 + +psplinkSetK1: + move $v0, $k1 + jr $ra + move $k1, $a0 + + .end psplinkSetK1 + + .global psplinkGetCop0 + .ent psplinkGetCop0 + +psplinkGetCop0: + mfc0 $v0, $0 + sw $v0, 0($a0) + mfc0 $v0, $1 + sw $v0, 4($a0) + mfc0 $v0, $2 + sw $v0, 8($a0) + mfc0 $v0, $3 + sw $v0, 12($a0) + mfc0 $v0, $4 + sw $v0, 16($a0) + mfc0 $v0, $5 + sw $v0, 20($a0) + mfc0 $v0, $6 + sw $v0, 24($a0) + mfc0 $v0, $7 + sw $v0, 28($a0) + mfc0 $v0, $8 + sw $v0, 32($a0) + mfc0 $v0, $9 + sw $v0, 36($a0) + mfc0 $v0, $10 + sw $v0, 40($a0) + mfc0 $v0, $11 + sw $v0, 44($a0) + mfc0 $v0, $12 + sw $v0, 48($a0) + mfc0 $v0, $13 + sw $v0, 52($a0) + mfc0 $v0, $14 + sw $v0, 56($a0) + mfc0 $v0, $15 + sw $v0, 60($a0) + mfc0 $v0, $16 + sw $v0, 64($a0) + mfc0 $v0, $17 + sw $v0, 68($a0) + mfc0 $v0, $18 + sw $v0, 72($a0) + mfc0 $v0, $19 + sw $v0, 76($a0) + mfc0 $v0, $20 + sw $v0, 80($a0) + mfc0 $v0, $21 + sw $v0, 84($a0) + mfc0 $v0, $22 + sw $v0, 88($a0) + mfc0 $v0, $23 + sw $v0, 92($a0) + mfc0 $v0, $24 + sw $v0, 96($a0) + mfc0 $v0, $25 + sw $v0, 100($a0) + mfc0 $v0, $26 + sw $v0, 104($a0) + mfc0 $v0, $27 + sw $v0, 108($a0) + mfc0 $v0, $28 + sw $v0, 112($a0) + mfc0 $v0, $29 + sw $v0, 116($a0) + mfc0 $v0, $30 + sw $v0, 120($a0) + mfc0 $v0, $31 + sw $v0, 124($a0) + addiu $a0, $a0, 128 + cfc0 $v0, $0 + sw $v0, 0($a0) + cfc0 $v0, $1 + sw $v0, 4($a0) + cfc0 $v0, $2 + sw $v0, 8($a0) + cfc0 $v0, $3 + sw $v0, 12($a0) + cfc0 $v0, $4 + sw $v0, 16($a0) + cfc0 $v0, $5 + sw $v0, 20($a0) + cfc0 $v0, $6 + sw $v0, 24($a0) + cfc0 $v0, $7 + sw $v0, 28($a0) + cfc0 $v0, $8 + sw $v0, 32($a0) + cfc0 $v0, $9 + sw $v0, 36($a0) + cfc0 $v0, $10 + sw $v0, 40($a0) + cfc0 $v0, $11 + sw $v0, 44($a0) + cfc0 $v0, $12 + sw $v0, 48($a0) + cfc0 $v0, $13 + sw $v0, 52($a0) + cfc0 $v0, $14 + sw $v0, 56($a0) + cfc0 $v0, $15 + sw $v0, 60($a0) + cfc0 $v0, $16 + sw $v0, 64($a0) + cfc0 $v0, $17 + sw $v0, 68($a0) + cfc0 $v0, $18 + sw $v0, 72($a0) + cfc0 $v0, $19 + sw $v0, 76($a0) + cfc0 $v0, $20 + sw $v0, 80($a0) + cfc0 $v0, $21 + sw $v0, 84($a0) + cfc0 $v0, $22 + sw $v0, 88($a0) + cfc0 $v0, $23 + sw $v0, 92($a0) + cfc0 $v0, $24 + sw $v0, 96($a0) + cfc0 $v0, $25 + sw $v0, 100($a0) + cfc0 $v0, $26 + sw $v0, 104($a0) + cfc0 $v0, $27 + sw $v0, 108($a0) + cfc0 $v0, $28 + sw $v0, 112($a0) + cfc0 $v0, $29 + sw $v0, 116($a0) + cfc0 $v0, $30 + sw $v0, 120($a0) + cfc0 $v0, $31 + sw $v0, 124($a0) + jr $ra + nop + + .end psplinkGetCop0 + + .global _apiHookHandle + .global _apiHookReturn + + .global _apiHookEntry + .ent _apiHookEntry +_apiHookEntry: + addiu $sp, $sp, -(4*8) # Allocate space for 6 args + $ra + hooknum + sw $ra, 0($sp) # Save $ra and args + sw $a0, 4($sp) + sw $a1, 8($sp) + sw $a2, 12($sp) + sw $a3, 16($sp) + sw $t0, 20($sp) + sw $t1, 24($sp) + sw $v0, 28($sp) + move $a0, $v0 # Copy api hook number into first arg + jal _apiHookHandle + addiu $a1, $sp, 4 # Copy pointer to args into second arg + + lw $a0, 4($sp) # Restore args + lw $a1, 8($sp) + lw $a2, 12($sp) + lw $a3, 16($sp) + + beq $v0, $0, 1f + lw $t0, 20($sp) + + jalr $v0 # Will call with a 32 byte overhead + lw $t1, 24($sp) + + sw $v0, 4($sp) # Save return values + sw $v1, 8($sp) + lw $a0, 28($sp) # Load hook address + + jal _apiHookReturn # Print return values + addiu $a1, $sp, 4 # Copy pointer to return values into second arg + + lw $v0, 4($sp) # Restore return values + lw $v1, 8($sp) + +1: + lw $ra, 0($sp) # Restore $ra + jr $ra + addiu $sp, $sp, (4*8) + .end _apiHookEntry + + .global _apiHook0 + .ent _apiHook0 +_apiHook0: + j _apiHookEntry + li $v0, 0 + .end _apiHook0 + + .global _apiHook1 + .ent _apiHook1 +_apiHook1: + j _apiHookEntry + li $v0, 1 + .end _apiHook1 + + .global _apiHook2 + .ent _apiHook2 +_apiHook2: + j _apiHookEntry + li $v0, 2 + .end _apiHook2 + + .global _apiHook3 + .ent _apiHook3 +_apiHook3: + j _apiHookEntry + li $v0, 3 + .end _apiHook3 + + .global _apiHook4 + .ent _apiHook4 +_apiHook4: + j _apiHookEntry + li $v0, 4 + .end _apiHook4 + + .global _apiHook5 + .ent _apiHook5 +_apiHook5: + j _apiHookEntry + li $v0, 5 + .end _apiHook5 + + .global _apiHook6 + .ent _apiHook6 +_apiHook6: + j _apiHookEntry + li $v0, 6 + .end _apiHook6 + + .global _apiHook7 + .ent _apiHook7 +_apiHook7: + j _apiHookEntry + li $v0, 7 + .end _apiHook7 + + .global _apiHook8 + .ent _apiHook8 +_apiHook8: + j _apiHookEntry + li $v0, 8 + .end _apiHook8 + + .global _apiHook9 + .ent _apiHook9 +_apiHook9: + j _apiHookEntry + li $v0, 9 + .end _apiHook9 + + .global _apiHook10 + .ent _apiHook10 +_apiHook10: + j _apiHookEntry + li $v0, 10 + .end _apiHook10 + + .global _apiHook11 + .ent _apiHook11 +_apiHook11: + j _apiHookEntry + li $v0, 11 + .end _apiHook11 + + .global _apiHook12 + .ent _apiHook12 +_apiHook12: + j _apiHookEntry + li $v0, 12 + .end _apiHook12 + + .global _apiHook13 + .ent _apiHook13 +_apiHook13: + j _apiHookEntry + li $v0, 13 + .end _apiHook13 + + .global _apiHook14 + .ent _apiHook14 +_apiHook14: + j _apiHookEntry + li $v0, 14 + .end _apiHook14 + + .global _apiHook15 + .ent _apiHook15 +_apiHook15: + j _apiHookEntry + li $v0, 15 + .end _apiHook15 diff --git a/psplink/libs.c b/psplink/libs.c new file mode 100644 index 0000000..6abdf7a --- /dev/null +++ b/psplink/libs.c @@ -0,0 +1,356 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * libs.c - Module library code for psplink. + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/libs.c $ + * $Id: libs.c 2301 2007-08-26 13:48:05Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "psplink.h" +#include "libs.h" + +struct PspModuleImport +{ + const char *name; + unsigned short version; + unsigned short attribute; + unsigned char entLen; + unsigned char varCount; + unsigned short funcCount; + unsigned int *fnids; + unsigned int *funcs; + unsigned int *vnids; + unsigned int *vars; +} __attribute__((packed)); + +static struct SceLibraryEntryTable *_libsFindLibrary(SceUID uid, const char *library) +{ + struct SceLibraryEntryTable *entry; + SceModule *pMod; + void *entTab; + int entLen; + + pMod = sceKernelFindModuleByUID(uid); + if(pMod != NULL) + { + int i = 0; + + entTab = pMod->ent_top; + entLen = pMod->ent_size; + while(i < entLen) + { + entry = (struct SceLibraryEntryTable *) (entTab + i); + + if((entry->libname) && (strcmp(entry->libname, library) == 0)) + { + return entry; + } + else if(!entry->libname && !library) + { + return entry; + } + + i += (entry->len * 4); + } + } + + return NULL; +} + +static struct PspModuleImport *_libsFindImport(SceUID uid, const char *library) +{ + SceModule *pMod; + void *stubTab; + int stubLen; + + pMod = sceKernelFindModuleByUID(uid); + if(pMod != NULL) + { + int i = 0; + + stubTab = pMod->stub_top; + stubLen = pMod->stub_size; + while(i < stubLen) + { + struct PspModuleImport *pImp = (struct PspModuleImport *) (stubTab + i); + + if((pImp->name) && (strcmp(pImp->name, library) == 0)) + { + return pImp; + } + + i += (pImp->entLen * 4); + } + } + + return NULL; +} + +void* libsFindExportAddrByNid(SceUID uid, const char *library, unsigned int nid) +{ + struct SceLibraryEntryTable *entry; + unsigned int *addr = NULL; + + entry = _libsFindLibrary(uid, library); + if(entry) + { + int count; + int total; + unsigned int *vars; + + total = entry->stubcount + entry->vstubcount; + vars = entry->entrytable; + + if(entry->stubcount > 0) + { + for(count = 0; count < entry->stubcount; count++) + { + if(vars[count] == nid) + { + return &vars[count+total]; + } + } + } + } + + return addr; +} + +unsigned int libsNameToNid(const char *name) +{ + u8 digest[20]; + unsigned int nid; + + if(sceKernelUtilsSha1Digest((u8 *) name, strlen(name), digest) >= 0) + { + nid = digest[0] | (digest[1] << 8) | (digest[2] << 16) | (digest[3] << 24); + return nid; + } + + return 0; +} + +void* libsFindExportAddrByName(SceUID uid, const char *library, const char *name) +{ + unsigned int nid; + + nid = libsNameToNid(name); + + return libsFindExportAddrByNid(uid, library, nid); +} + +unsigned int libsFindExportByName(SceUID uid, const char *library, const char *name) +{ + unsigned int *addr; + + addr = libsFindExportAddrByName(uid, library, name); + if(!addr) + { + return 0; + } + + return *addr; +} + +unsigned int libsFindExportByNid(SceUID uid, const char *library, unsigned int nid) +{ + unsigned int *addr; + + addr = libsFindExportAddrByNid(uid, library, nid); + if(!addr) + { + return 0; + } + + return *addr; +} + +unsigned int libsFindImportAddrByNid(SceUID uid, const char *library, unsigned int nid) +{ + struct PspModuleImport *pImp; + + pImp = _libsFindImport(uid, library); + if(pImp) + { + int i; + + for(i = 0; i < pImp->funcCount; i++) + { + if(pImp->fnids[i] == nid) + { + return (unsigned int) &pImp->funcs[i*2]; + } + } + } + + return 0; +} + +unsigned int libsFindImportAddrByName(SceUID uid, const char *library, const char *name) +{ + unsigned int nid; + + nid = libsNameToNid(name); + + return libsFindImportAddrByNid(uid, library, nid); +} + +int libsPatchFunction(SceUID uid, const char *library, unsigned int nid, u16 retval) +{ + unsigned int* addr; + int intc; + int ret = 0; + + intc = pspSdkDisableInterrupts(); + addr = (unsigned int *) libsFindExportByNid(uid, library, nid); + if(addr) + { + addr[0] = 0x03E00008; + addr[1] = 0x24020000 | (unsigned int) retval; + sceKernelDcacheWritebackInvalidateRange(addr, 8); + sceKernelIcacheInvalidateRange(addr, 8); + ret = 1; + } + pspSdkEnableInterrupts(intc); + + return ret; +} + +int libsPrintImports(SceUID uid) +{ + SceModule *pMod; + void *stubTab; + int stubLen; + + pMod = sceKernelFindModuleByUID(uid); + if(pMod != NULL) + { + int i = 0; + + stubTab = pMod->stub_top; + stubLen = pMod->stub_size; + SHELL_PRINT("stubTab %p - stubLen %d\n", stubTab, stubLen); + while(i < stubLen) + { + int count; + struct PspModuleImport *pImp = (struct PspModuleImport *) (stubTab + i); + + if(pImp->name) + { + SHELL_PRINT("Import Library %s, attr 0x%04X\n", pImp->name, pImp->attribute); + } + else + { + SHELL_PRINT("Import Library %s, attr 0x%04X\n", "Unknown", pImp->attribute); + } + + if(pImp->funcCount > 0) + { + SHELL_PRINT("Function Imports:\n"); + for(count = 0; count < pImp->funcCount; count++) + { + SHELL_PRINT("Entry %-3d: UID 0x%08X, Function 0x%08X\n", count+1, pImp->fnids[count], + (unsigned int) &pImp->funcs[count*2]); + } + } + + if(pImp->funcCount > 0) + { + SHELL_PRINT("Variable Imports:\n"); + for(count = 0; count < pImp->varCount; count++) + { + SHELL_PRINT("Entry %-3d: UID 0x%08X, Variable 0x%08X\n", count+1, pImp->vnids[count], + (unsigned int) &pImp->vars[count*2]); + } + } + SHELL_PRINT("\n"); + + i += (pImp->entLen * 4); + } + } + else + { + return 0; + } + + return 1; +} + +int libsPrintEntries(SceUID uid) +{ + struct SceLibraryEntryTable *entry; + SceModule *pMod; + void *entTab; + int entLen; + + pMod = sceKernelFindModuleByUID(uid); + if(pMod != NULL) + { + int i = 0; + + entTab = pMod->ent_top; + entLen = pMod->ent_size; + SHELL_PRINT("entTab %p - entLen %d\n", entTab, entLen); + while(i < entLen) + { + int count; + int total; + unsigned int *vars; + + entry = (struct SceLibraryEntryTable *) (entTab + i); + + if(entry->libname) + { + SHELL_PRINT("Export Library %s, attr 0x%04X\n", entry->libname, entry->attribute); + } + else + { + SHELL_PRINT("Export library %s, attr 0x%04X\n", "syslib", entry->attribute); + } + total = entry->stubcount + entry->vstubcount; + vars = entry->entrytable; + + if(entry->stubcount > 0) + { + SHELL_PRINT("Function Exports:\n"); + for(count = 0; count < entry->stubcount; count++) + { + SHELL_PRINT("Entry %-3d: UID 0x%08X, Function 0x%08X\n", count+1, vars[count], vars[count+total]); + } + } + + if(entry->vstubcount > 0) + { + SHELL_PRINT("Variable Exports:\n"); + for(count = 0; count < entry->vstubcount; count++) + { + SHELL_PRINT("Entry %-3d: UID 0x%08X, Variable 0x%08X\n", count+1, vars[count+entry->stubcount], + vars[count+entry->stubcount+total]); + } + } + SHELL_PRINT("\n"); + + i += (entry->len * 4); + } + } + else + { + return 0; + } + + return 1; +} diff --git a/psplink/libs.h b/psplink/libs.h new file mode 100644 index 0000000..adaeb64 --- /dev/null +++ b/psplink/libs.h @@ -0,0 +1,27 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * libs.h - Module library code for psplink + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/libs.h $ + * $Id: libs.h 2301 2007-08-26 13:48:05Z tyranid $ + */ +#ifndef __LIBS_H__ +#define __LIBS_H__ + +int libsPrintEntries(SceUID uid); +int libsPrintImports(SceUID uid); +unsigned int libsFindExportByName(SceUID uid, const char *library, const char *name); +unsigned int libsFindExportByNid(SceUID uid, const char *library, unsigned int nid); +void* libsFindExportAddrByName(SceUID uid, const char *library, const char *name); +void* libsFindExportAddrByNid(SceUID uid, const char *library, unsigned int nid); +int libsPatchFunction(SceUID uid, const char *library, unsigned int nid, u16 retval); +unsigned int libsNameToNid(const char *name); +unsigned int libsFindImportAddrByName(SceUID uid, const char *library, const char *name); +unsigned int libsFindImportAddrByNid(SceUID uid, const char *library, unsigned int nid); + +#endif diff --git a/psplink/main.c b/psplink/main.c new file mode 100644 index 0000000..e904d84 --- /dev/null +++ b/psplink/main.c @@ -0,0 +1,392 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK kernel module main code. + * + * Copyright (c) 2005 James F + * Copyright (c) 2005 Julian T + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/main.c $ + * $Id: main.c 2322 2007-09-30 17:49:32Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "memoryUID.h" +#include "psplink.h" +#include "psplinkcnf.h" +#include "debug.h" +#include "util.h" +#include "shell.h" +#include "config.h" +#include "exception.h" +#include "apihook.h" +#include "tty.h" +#include "libs.h" +#include "modload.h" +#include "decodeaddr.h" + +PSP_MODULE_INFO("PSPLINK", 0x1000, 1, 1); + +#define BOOTLOADER_NAME "PSPLINKLOADER" + +struct GlobalContext g_context; + +void save_execargs(int argc, char **argv); + +int unload_loader(void) +{ + SceModule *mod; + SceUID modid; + int ret = 0; + int status; + + mod = sceKernelFindModuleByName(BOOTLOADER_NAME); + if(mod != NULL) + { + DEBUG_PRINTF("Loader UID: %08X\n", mod->modid); + /* Stop module */ + modid = mod->modid; + ret = sceKernelStopModule(modid, 0, NULL, &status, NULL); + if(ret >= 0) + { + ret = sceKernelUnloadModule(modid); + } + } + else + { + Kprintf("Couldn't find bootloader\n"); + } + + return 0; +} + +void parse_sceargs(SceSize args, void *argp, int *argc, char **argv) +{ + int loc = 0; + char *ptr = argp; + + *argc = 0; + while(loc < args) + { + argv[*argc] = &ptr[loc]; + loc += strlen(&ptr[loc]) + 1; + (*argc)++; + if(*argc == (MAX_ARGS-1)) + { + break; + } + } + + argv[*argc] = NULL; +} + +void load_psplink_user(const char *bootpath) +{ + char prx_path[MAXPATHLEN]; + + strcpy(prx_path, bootpath); + strcat(prx_path, "psplink_user.prx"); + load_start_module(prx_path, 0, NULL); +} + +SceUID load_gdb(const char *bootpath, int argc, char **argv) +{ + char prx_path[MAXPATHLEN]; + + strcpy(prx_path, bootpath); + strcat(prx_path, "usbgdb.prx"); + g_context.gdb = 1; + return load_start_module(prx_path, argc, argv); +} + +int reset_thread(SceSize args, void *argp) +{ + psplinkReset(); + + return 0; +} + +void exit_reset(void) +{ + psplinkSetK1(0); + + if(g_context.resetonexit) + { + /* Create a new thread to do the reset */ + SceUID thid; + + thid = sceKernelCreateThread("PspLinkReset", reset_thread, 8, 4*1024, 0, NULL); + if(thid >= 0) + { + sceKernelStartThread(thid, 0, NULL); + } + } + else + { + SHELL_PRINT("\nsceKernelExitGame caught!\n"); + /* Kill the thread, bad idea to drop back to the program */ + } + + sceKernelExitThread(0); +} + +void psplinkStop(void) +{ + if(g_context.thevent >= 0) + { + sceKernelReleaseThreadEventHandler(g_context.thevent); + } +} + +void psplinkReset(void) +{ +#if _PSP_FW_VERSION >= 200 + { + struct SceKernelLoadExecVSHParam param; + const char *rebootkey = NULL; + char argp[256]; + int args; + + args = 0; + strcpy(argp, g_context.bootfile); + args += strlen(g_context.bootfile)+1; + strcpy(&argp[args], g_context.currdir); + args += strlen(g_context.currdir)+1; + + memset(¶m, 0, sizeof(param)); + param.size = sizeof(param); + param.args = args; + param.argp = argp; + switch(g_context.rebootkey) + { + case REBOOT_MODE_GAME: rebootkey = "game"; + break; + case REBOOT_MODE_VSH : rebootkey = "vsh"; + break; + case REBOOT_MODE_UPDATER : rebootkey = "updater"; + break; + default: rebootkey = NULL; + break; + + }; + param.key = rebootkey; + param.vshmain_args_size = 0; + param.vshmain_args = NULL; + + debugDisableHW(); + psplinkSetK1(0); + SHELL_PRINT("Resetting psplink\n"); + psplinkStop(); + + sceKernelSuspendAllUserThreads(); + + sceKernelLoadExecVSHMs2(g_context.bootfile, ¶m); + } +#else + { + struct SceKernelLoadExecParam le; + struct SavedContext *save = (struct SavedContext *) SAVED_ADDR; + const char *rebootkey = NULL; + + save->magic = SAVED_MAGIC; + strcpy(save->currdir, g_context.currdir); + save->rebootkey = g_context.rebootkey; + + debugDisableHW(); + psplinkSetK1(0); + SHELL_PRINT("Resetting psplink\n"); + psplinkStop(); + + le.size = sizeof(le); + le.args = strlen(g_context.bootfile) + 1; + le.argp = (char *) g_context.bootfile; + switch(g_context.rebootkey) + { + case REBOOT_MODE_GAME: rebootkey = "game"; + break; + case REBOOT_MODE_VSH : rebootkey = "vsh"; + break; + case REBOOT_MODE_UPDATER : rebootkey = "updater"; + break; + default: rebootkey = NULL; + break; + + }; + le.key = rebootkey; + + sceKernelSuspendAllUserThreads(); + + sceKernelLoadExec(g_context.bootfile, &le); + } +#endif +} + +void psplinkExitShell(void) +{ +#if _PSP_FW_VERSION >= 200 + { + sceKernelExitVSHVSH(NULL); + } +#else + { + sceKernelExitGame(); + } +#endif +} + +int psplinkPresent(void) +{ + return 1; +} + +int RegisterExceptionDummy(void) +{ + return 0; +} + +/* Patch out the exception handler setup call for apps which come after us ;P */ +int psplinkPatchException(void) +{ + unsigned int *addr; + int intc; + + intc = pspSdkDisableInterrupts(); + addr = libsFindExportAddrByNid(refer_module_by_name("sceExceptionManager", NULL), "ExceptionManagerForKernel", 0x565C0B0E); + if(addr) + { + *addr = (unsigned int) RegisterExceptionDummy; + sceKernelDcacheWritebackInvalidateRange(addr, 4); + sceKernelIcacheInvalidateRange(addr, 4); + } + pspSdkEnableInterrupts(intc); + + return 0; +} + +void initialise(SceSize args, void *argp) +{ + struct ConfigContext ctx; + const char *init_dir = "host0:/"; + int (*g_sceUmdActivate)(int, const char *); + int argc; + char *argv[MAX_ARGS]; + + memset(&g_context, 0, sizeof(g_context)); + map_firmwarerev(); + exceptionInit(); + g_context.thevent = -1; + parse_sceargs(args, argp, &argc, argv); + + if(argc > 0) + { + char *lastdir; + + g_context.bootfile = argv[0]; + lastdir = strrchr(argv[0], '/'); + if(lastdir != NULL) + { + memcpy(g_context.bootpath, argv[0], lastdir - argv[0] + 1); + } + } + + configLoad(g_context.bootpath, &ctx); + + if(ctx.pid) + { + g_context.pid = ctx.pid; + } + else + { + g_context.pid = HOSTFSDRIVER_PID; + } + + ttyInit(); + init_usbhost(g_context.bootpath); + +#if _PSP_FW_VERSION >= 200 + if(argc > 1) + { + init_dir = argv[1]; + } +#else + { + struct SavedContext *save = (struct SavedContext *) SAVED_ADDR; + if(save->magic == SAVED_MAGIC) + { + init_dir = save->currdir; + save->magic = 0; + g_context.rebootkey = save->rebootkey; + } + } +#endif + + if(shellInit(init_dir) < 0) + { + sceKernelExitGame(); + } + + g_sceUmdActivate = (void*) libsFindExportByNid(refer_module_by_name("sceUmd_driver", NULL), + "sceUmdUser", 0xC6183D47); + if(g_sceUmdActivate) + { + g_sceUmdActivate(1, "disc0:"); + } + + /* Hook sceKernelExitGame */ + apiHookByNid(refer_module_by_name("sceLoadExec", NULL), "LoadExecForUser", 0x05572A5F, exit_reset); + + unload_loader(); + + psplinkPatchException(); + + if(ctx.enableuser) + { + load_psplink_user(g_context.bootpath); + } + + g_context.resetonexit = ctx.resetonexit; + + sceKernelRegisterDebugPutchar(NULL); + enable_kprintf(1); + debugHwInit(); + modLoad(g_context.bootpath); +} + +/* Simple thread */ +int main_thread(SceSize args, void *argp) +{ + initialise(args, argp); + + shellParseThread(0, NULL); + sceKernelSleepThread(); + + return 0; +} + +/* Entry point */ +int module_start(SceSize args, void *argp) +{ + int thid; + + /* Create a high priority thread */ + thid = sceKernelCreateThread("PspLink", main_thread, 8, 64*1024, 0, NULL); + if(thid >= 0) + { + sceKernelStartThread(thid, args, argp); + } + + return 0; +} diff --git a/psplink/memoryUID.c b/psplink/memoryUID.c new file mode 100644 index 0000000..dd158dc --- /dev/null +++ b/psplink/memoryUID.c @@ -0,0 +1,161 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * memoryUID.c - Code to dump the UID table. + * + * Copyright (c) 2005 John_K + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/memoryUID.c $ + * $Id: memoryUID.c 2115 2006-12-30 14:50:17Z tyranid $ + */ +#include +#include +#include +#include +#include "psplink.h" +#include "memoryUID.h" + +uidControlBlock* findUIDObject(SceUID uid, const char *name, const char *parent) +{ + uidControlBlock *entry; + uidControlBlock *end; + + if(sceKernelDevkitVersion() == 0x01000300) + { + entry = (uidControlBlock *) UIDLIST_START_1_0; + } + else + { + entry = SysMemForKernel_536AD5E1(); + } + /* + if(sceKernelDevkitVersion() == 0x01050001) + entry = (uidControlBlock *) UIDLIST_START_1_5; + else + entry = (uidControlBlock *) UIDLIST_START_1_0; + */ + entry = entry->parent; + end = entry; + entry = entry->nextEntry; + + do { + if(entry->UID == uid) + return entry; + if (entry->nextChild != entry) { + do { + uidControlBlock *ret = NULL; + entry = entry->nextChild; + if(name) + { + if (strcmp(entry->name, name) == 0) + ret = entry; + } + else + { + if(entry->UID == uid) + ret = entry; + } + + if(ret) + { + if(parent && ret->type) + { + if(strcmp(parent, ret->type->name) == 0) + { + return ret; + } + } + else + { + return ret; + } + } + + } while (entry->nextChild != entry->type); + entry = entry->nextChild; + } + entry = entry->nextEntry; + } while (entry->nextEntry != end); //Stop at 'Basic' entry like Sysmem does, just not in the same way ;) + return 0; +} + +SceUID findUIDByName(const char *name) +{ + uidControlBlock *entry = findUIDObject(0, name, NULL); + if(entry) + { + return entry->UID; + } + + return -1; +} + +void printUIDEntry(uidControlBlock *entry) +{ + if(entry) + { + SHELL_PRINT("(UID): 0x%08X, (entry): 0x%p, (size): %d, (attr): 0x%X, (Name): %s\n", entry->UID, entry, entry->size << 2, entry->attribute, entry->name); + } +} + +void printUIDList(const char *name) +{ + uidControlBlock *entry; + uidControlBlock *end; + int cmp = 0; + + if(sceKernelDevkitVersion() == 0x01000300) + { + entry = (uidControlBlock *) UIDLIST_START_1_0; + } + else + { + entry = SysMemForKernel_536AD5E1(); + } + + /* + if(sceKernelDevkitVersion() == 0x01050001) + entry = (uidControlBlock *) UIDLIST_START_1_5; + else + entry = (uidControlBlock *) UIDLIST_START_1_0; + */ + entry = entry->parent; + end = entry; + entry = entry->nextEntry; + //printf("************ MY UID LIST START ************\n"); + do { + if(name) + { + cmp = strcmp(entry->name, name); + } + + if(cmp == 0) + { + SHELL_PRINT("\n[%s] UID 0x%08X (attr 0x%X entry 0x%p)\n", entry->name, entry->UID, entry->attribute, entry); + } + + if (entry->nextChild == entry) { + if(cmp == 0) + { + SHELL_PRINT(" \n"); + } + + } else { + do { + entry = entry->nextChild; + if(cmp == 0) + { + //printf("(Name): %31s, (UID): 0x%08X, (entry): 0x%p (attr): 0x%X \n", entry->name, entry->UID, entry, entry->attribute); + printUIDEntry(entry); + } + } while (entry->nextChild != entry->type); + entry = entry->nextChild; + } + + entry = entry->nextEntry; + } while (entry->nextEntry != end); //Stop at 'Basic' entry like Sysmem does but not in the same way ;) + //printf("************ MY UID LIST END ************\n"); +} + diff --git a/psplink/memoryUID.h b/psplink/memoryUID.h new file mode 100644 index 0000000..41f5abd --- /dev/null +++ b/psplink/memoryUID.h @@ -0,0 +1,31 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * memoryUID.h - Header file for UID dumper. + * + * Copyright (c) 2005 John_K + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/memoryUID.h $ + * $Id: memoryUID.h 2115 2006-12-30 14:50:17Z tyranid $ + */ +#ifndef __MEMORYUID_H__ +#define __MEMORYUID_H__ + +#include + +#define UIDLIST_START_1_0 0x8800d030 +#define UIDLIST_START_1_5 0x8800fc98 + +uidControlBlock* findUIDObject(SceUID uid, const char *name, const char *parent); +SceUID findUIDByName(const char *name); +void printUIDList(const char *name); +void printUIDEntry(uidControlBlock *entry); + +#define findObjectByUID(uid) findUIDObject(uid, NULL, NULL) +#define findObjectByName(name) findUIDObject(0, name, NULL) +#define findObjectByUIDWithParent(uid, parent) findUIDObject(uid, NULL, parent) +#define findObjectByNameWithParent(name, parent) findUIDObject(0, name, parent) + +#endif diff --git a/psplink/modload.c b/psplink/modload.c new file mode 100644 index 0000000..7bc0fe1 --- /dev/null +++ b/psplink/modload.c @@ -0,0 +1,50 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * modload.c - PSPLINK kernel module loader. + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/modload.c $ + * $Id: modload.c 2157 2007-01-29 22:50:26Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "memoryUID.h" +#include "psplink.h" +#include "util.h" + +void modLoad(const char *bootpath) +{ + PspFile fmod; + char buf[1024]; + char ini_path[1024]; + + strcpy(ini_path, bootpath); + strcat(ini_path, "modload.ini"); + + if(openfile(ini_path, &fmod)) + { + while(fdgets(&fmod, buf, sizeof(buf))) + { + strip_whitesp(buf); + if(buf[0]) + { + (void) load_start_module(buf, 0, NULL); + } + } + closefile(&fmod); + } +} diff --git a/psplink/modload.h b/psplink/modload.h new file mode 100644 index 0000000..8efc69d --- /dev/null +++ b/psplink/modload.h @@ -0,0 +1,19 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * modload.h - PSPLINK kernel module oader. + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/modload.h $ + * $Id: modload.h 2039 2006-10-23 21:58:11Z tyranid $ + */ + +#ifndef __PSPLINKMODLOAD_H__ +#define __PSPLINKMODLOAD_H__ + +void modLoad(const char *bootpath); + +#endif diff --git a/psplink/psplink.h b/psplink/psplink.h new file mode 100644 index 0000000..ce5a7b1 --- /dev/null +++ b/psplink/psplink.h @@ -0,0 +1,95 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * psplink.h - PSPLINK global header file. + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/psplink.h $ + * $Id: psplink.h 2303 2007-08-26 17:10:21Z tyranid $ + */ + +#ifndef __PSPLINK_H +#define __PSPLINK_H + +#include +#include "pspstdio.h" + +/* Event flags */ +#define EVENT_SIO 0x01 +#define EVENT_INIT 0x10 +#define EVENT_RESUMESH 0x100 +#define EVENT_RESET 0x200 + +#define MAXPATHLEN 1024 +#define MAX_ARGS 16 + +#define DEFAULT_BAUDRATE 115200 + +#ifdef DEBUG +#define DEBUG_START { int fd; fd = sceIoOpen("ms0:/debug.txt", PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0666); sceIoClose(fd); } +#define DEBUG_PRINTF(fmt, ...) \ +{ \ + int fd; \ + fd = sceIoOpen("ms0:/debug.txt", PSP_O_WRONLY | PSP_O_APPEND, 0666); \ + fdprintf(fd, fmt, ## __VA_ARGS__); \ + sceIoClose(fd); \ +} +#else +#define DEBUG_START +#define DEBUG_PRINTF(fmt, ...) +#endif + +typedef unsigned int jmp_buf[12]; +int setjmp(jmp_buf jmp); +int longjmp(jmp_buf jmp, int ret); + +int shprintf(const char *fmt, ...); + +#define SHELL_PRINT(fmt, ...) shprintf("\xff" fmt "\xfe", ## __VA_ARGS__) +#define SHELL_PRINT_CMD(cmd, fmt, ...) shprintf("\xff" "%c" fmt "\xfe", cmd, ## __VA_ARGS__) + +void psplinkReset(void); +void psplinkStop(void); +unsigned int psplinkSetK1(unsigned int k1); +void psplinkGetCop0(unsigned int *regs); +int psplinkParseComamnd(char *command); +void psplinkExitShell(void); +SceUID load_gdb(const char *bootpath, int argc, char **argv); + +struct ConfigContext; +struct GlobalContext; + +#define SAVED_MAGIC 0xBAA1A11C +#define SAVED_ADDR 0x883F0000 + +#define REBOOT_MODE_GAME 0 +#define REBOOT_MODE_VSH 1 +#define REBOOT_MODE_UPDATER 2 + +struct SavedContext +{ + uint32_t magic; + char currdir[MAXPATHLEN]; + int rebootkey; +}; + +struct GlobalContext +{ + /* The filename of the bootstrap */ + const char *bootfile; + /* The boot path */ + char bootpath[MAXPATHLEN]; + /* Indicates the current directory */ + char currdir[MAXPATHLEN]; + int resetonexit; + SceUID thevent; + int gdb; + int pid; + int rebootkey; + jmp_buf parseenv; +}; + +#endif diff --git a/psplink/psplink.ini b/psplink/psplink.ini new file mode 100644 index 0000000..6d429cb --- /dev/null +++ b/psplink/psplink.ini @@ -0,0 +1,12 @@ +# Example psplink configuration file. + +# pid=num Set the product ID for hostfs allows you to use multiple PSPs at one time +# Must specify the PID using the -p option of usbhostfs_pc +# pid=0x1C9 + +# pluser=[0 1] Enable the PSPLink user module +pluser=0 + +# resetonexit=[0 1] Specify wheher to reset psplink when sceKernelExitGame +# is called +resetonexit=1 diff --git a/psplink/psplinkcnf.c b/psplink/psplinkcnf.c new file mode 100644 index 0000000..c9ffaee --- /dev/null +++ b/psplink/psplinkcnf.c @@ -0,0 +1,114 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * psplinkcnf.c - Functions to manipulate the configuration file + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/psplinkcnf.c $ + * $Id: psplinkcnf.c 1766 2006-01-29 00:03:02Z tyranid $ + */ +#include +#include +#include +#include "psplink.h" +#include "psplinkcnf.h" +#include "util.h" + +int psplinkConfigOpen(const char *filename, struct ConfigFile *cnf) +{ + int iRet = 0; + + do + { + if(cnf == NULL) + { + Kprintf("Error, invalid configuration structure\n"); + break; + } + + memset(cnf, 0, sizeof(struct ConfigFile)); + + if(openfile(filename, &cnf->file)) + { + iRet = 1; + } + } + while(0); + + return iRet; +} + +void psplinkConfigClose(struct ConfigFile *cnf) +{ + do + { + if(cnf == NULL) + { + Kprintf("Error, invalid configuration structure\n"); + break; + } + + closefile(&cnf->file); + } + while(0); +} + +const char* psplinkConfigReadNext(struct ConfigFile *cnf, const char **name) +{ + const char *pVal = NULL; + + do + { + if(cnf == NULL) + { + Kprintf("Error, invalid configuration structure\n"); + break; + } + + while((pVal == NULL) && (fdgets(&cnf->file, cnf->str_buf, MAX_BUFFER))) + { + char *eq_pos; + + cnf->line++; + strip_whitesp(cnf->str_buf); + if((cnf->str_buf[0] == '#') || (cnf->str_buf[0] == 0)) + { + continue; + } + + eq_pos = strchr(cnf->str_buf, '='); + if(eq_pos == NULL) + { + Kprintf("Error on line %d of configuration file. No '='\n", cnf->line); + continue; + } + + *eq_pos = 0; + *name = cnf->str_buf; + eq_pos++; + if(*eq_pos == '"') + { + char *qend; + eq_pos++; + qend = strchr(eq_pos, '"'); + if(qend == NULL) + { + Kprintf("Error on line %d of configuration file. No closing quote\n", cnf->line); + continue; + } + else + { + *qend = 0; + } + } + pVal = eq_pos; + } + } + while(0); + + return pVal; +} + diff --git a/psplink/psplinkcnf.h b/psplink/psplinkcnf.h new file mode 100644 index 0000000..bc80206 --- /dev/null +++ b/psplink/psplinkcnf.h @@ -0,0 +1,27 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * psplinkcnf.c - Functions to manipulate the configuration file + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/psplinkcnf.h $ + * $Id: psplinkcnf.h 1818 2006-03-06 18:34:38Z tyranid $ + */ +#include +#include +#include "psplink.h" +#include "util.h" + +struct ConfigFile +{ + PspFile file; + char str_buf[MAX_BUFFER]; + int line; +}; + +int psplinkConfigOpen(const char *filename, struct ConfigFile *cnf); +const char* psplinkConfigReadNext(struct ConfigFile *cnf, const char **name); +void psplinkConfigClose(struct ConfigFile *cnf); diff --git a/psplink/pspstdio.h b/psplink/pspstdio.h new file mode 100644 index 0000000..8dbca7b --- /dev/null +++ b/psplink/pspstdio.h @@ -0,0 +1,25 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * stdio.h - Module library code for psplink + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://tyranid@svn.pspdev.org/psp/trunk/psplink/psplink/stdio.h $ + * $Id: stdio.h 1910 2006-05-14 18:39:19Z tyranid $ + */ +#ifndef __PSP_STDIO_H__ +#define __PSP_STDIO_H__ + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +int stdioTtyInit(void); +int stdioInstallStdinHandler(PspDebugInputHandler handler); +int stdioInstallStdoutHandler(PspDebugPrintHandler handler); +int stdioInstallStderrHandler(PspDebugPrintHandler handler); + +#endif diff --git a/psplink/shell.c b/psplink/shell.c new file mode 100644 index 0000000..afb0a75 --- /dev/null +++ b/psplink/shell.c @@ -0,0 +1,4251 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * shell.c - PSPLINK kernel module shell code + * + * Copyright (c) 2005 James F + * Copyright (c) 2005 Julian T + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/shell.c $ + * $Id: shell.c 2334 2007-11-03 16:47:05Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "memoryUID.h" +#include "psplink.h" +#include "psplinkcnf.h" +#include "util.h" +#include "bitmap.h" +#include "config.h" +#include "shell.h" +#include "version.h" +#include "exception.h" +#include "decodeaddr.h" +#include "debug.h" +#include "libs.h" +#include "thctx.h" +#include "apihook.h" +#include "tty.h" +#include "shellcmd.h" +#include "usbshell.h" + +#define MAX_SHELL_VAR 128 +#define MAX_CLI 4096 + +extern struct GlobalContext g_context; + +#define COMMAND_EVENT_DONE 1 + +typedef int (*threadmanprint_func)(SceUID uid, int verbose); + +struct call_frame +{ + u64 (*func)(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int e, unsigned int f); + unsigned int args[6]; +}; + +void psplinkPrintPrompt(void) +{ + SHELL_PRINT_CMD(SHELL_CMD_CWD, "%s", g_context.currdir); + SHELL_PRINT_CMD(SHELL_CMD_SUCCESS, "0x%08X", 0); +} + +static SceUID get_module_uid(const char *name) +{ + char *endp; + SceUID uid = -1; + + if(name[0] == '@') + { + uid = refer_module_by_name(&name[1], NULL); + } + else + { + uid = strtoul(name, &endp, 0); + if(*endp != 0) + { + SHELL_PRINT("ERROR: Invalid uid %s\n", name); + uid = -1; + } + } + + return uid; +} + +typedef int (*ReferFunc)(const char *, SceUID *, void *); + +static SceUID get_thread_uid(const char *name, ReferFunc pRefer) +{ + char *endp; + SceUID uid = -1; + + if(name[0] == '@') + { + if(pRefer(&name[1], &uid, NULL) < 0) + { + SHELL_PRINT("ERROR: Invalid name %s\n", name); + return CMD_ERROR; + } + } + else + { + uid = strtoul(name, &endp, 0); + if(*endp != 0) + { + SHELL_PRINT("ERROR: Invalid uid %s\n", name); + uid = -1; + } + } + + return uid; +} + +static int threadmanlist_cmd(int argc, char **argv, enum SceKernelIdListType type, const char *name, threadmanprint_func pinfo) +{ + SceUID ids[100]; + int ret; + int count; + int i; + int verbose = 0; + + if(argc > 0) + { + if(strcmp(argv[0], "v") == 0) + { + verbose = 1; + } + } + + memset(ids, 0, 100 * sizeof(SceUID)); + ret = sceKernelGetThreadmanIdList(type, ids, 100, &count); + if(ret >= 0) + { + SHELL_PRINT("<%s List (%d entries)>\n", name, count); + for(i = 0; i < count; i++) + { + if(pinfo(ids[i], verbose) < 0) + { + SHELL_PRINT("ERROR: Unknown %s 0x%08X\n", name, ids[i]); + } + } + } + + return CMD_OK; +} + +static int threadmaninfo_cmd(int argc, char **argv, const char *name, threadmanprint_func pinfo, ReferFunc pRefer) +{ + SceUID uid; + int ret = CMD_ERROR; + + uid = get_thread_uid(argv[0], pRefer); + + if(uid >= 0) + { + if(pinfo(uid, 1) < 0) + { + SHELL_PRINT("ERROR: Unknown %s 0x%08X\n", name, uid); + } + + ret = CMD_OK; + } + + return ret; +} + +static const char* get_thread_status(int stat, char *str) +{ + str[0] = 0; + if(stat & PSP_THREAD_RUNNING) + { + strcat(str, "RUNNING "); + } + + if(stat & PSP_THREAD_READY) + { + strcat(str, "READY "); + } + + if(stat & PSP_THREAD_WAITING) + { + strcat(str, "WAITING "); + } + + if(stat & PSP_THREAD_SUSPEND) + { + strcat(str, "SUSPEND "); + } + + if(stat & PSP_THREAD_STOPPED) + { + strcat(str, "STOPPED "); + } + + if(stat & PSP_THREAD_KILLED) + { + strcat(str, "KILLED "); + } + + return str; +} + +static int print_threadinfo(SceUID uid, int verbose) +{ + SceKernelThreadInfo info; + char status[256]; + char cwd[512]; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferThreadStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("Attr: 0x%08X - Status: %d/%s- Entry: 0x%p\n", info.attr, info.status, + get_thread_status(info.status, status), info.entry); + SHELL_PRINT("Stack: 0x%p - StackSize 0x%08X - GP: 0x%08X\n", info.stack, info.stackSize, + (unsigned int) info.gpReg); + SHELL_PRINT("InitPri: %d - CurrPri: %d - WaitType %d\n", info.initPriority, + info.currentPriority, info.waitType); + SHELL_PRINT("WaitId: 0x%08X - WakeupCount: %d - ExitStatus: 0x%08X\n", info.waitId, + info.wakeupCount, info.exitStatus); + SHELL_PRINT("RunClocks: %d - IntrPrempt: %d - ThreadPrempt: %d\n", info.runClocks.low, + info.intrPreemptCount, info.threadPreemptCount); + SHELL_PRINT("ReleaseCount: %d, StackFree: %d\n", info.releaseCount, sceKernelGetThreadStackFreeSize(uid)); + if(sceIoGetThreadCwd(uid, cwd, sizeof(cwd)) > 0) + { + SHELL_PRINT("Current Dir: %s\n", cwd); + } + } + } + + return ret; +} + +static int thlist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_Thread, "Thread", print_threadinfo); +} + +static int thsllist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_SleepThread, "Sleep Thread", print_threadinfo); +} + +static int thdelist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_DelayThread, "Delay Thread", print_threadinfo); +} + +static int thsulist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_SuspendThread, "Suspend Thread", print_threadinfo); +} + +static int thdolist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_DormantThread, "Dormant Thread", print_threadinfo); +} + +static int thinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "Thread", print_threadinfo, (ReferFunc) pspSdkReferThreadStatusByName); +} + +static int thread_do_cmd(const char *name, const char *type, ReferFunc refer, int (*fn)(SceUID uid)) +{ + SceUID uid; + int ret = CMD_ERROR; + int err; + + uid = get_thread_uid(name, refer); + + if(uid >= 0) + { + err = fn(uid); + if(err < 0) + { + SHELL_PRINT("Cannot %s uid 0x%08X (error: 0x%08X)\n", type, uid, err); + } + + ret = CMD_OK; + } + + return ret; +} + +static int thsusp_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "suspend", (ReferFunc) pspSdkReferThreadStatusByName, sceKernelSuspendThread); +} + +static int thspuser_cmd(int argc, char **argv, unsigned int *vRet) +{ + sceKernelSuspendAllUserThreads(); + + return CMD_OK; +} + +static int thresm_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "resume", (ReferFunc) pspSdkReferThreadStatusByName, sceKernelResumeThread); +} + +static int thwake_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "wakeup", (ReferFunc) pspSdkReferThreadStatusByName, sceKernelWakeupThread); +} + +static int thterm_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "terminate", (ReferFunc) pspSdkReferThreadStatusByName, sceKernelTerminateThread); +} + +static int thdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferThreadStatusByName, sceKernelDeleteThread); +} + +static int thtdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "terminate delete", (ReferFunc) pspSdkReferThreadStatusByName, sceKernelTerminateDeleteThread); +} + +static int thctx_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "get context", (ReferFunc) pspSdkReferThreadStatusByName, threadFindContext); +} + +static int thpri_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + int err; + unsigned int pri; + + uid = get_thread_uid(argv[0], (ReferFunc) pspSdkReferThreadStatusByName); + + if((uid >= 0) && (strtoint(argv[1], &pri))) + { + err = sceKernelChangeThreadPriority(uid, pri); + if(err < 0) + { + SHELL_PRINT("Cannot %s uid 0x%08X (error: 0x%08X)\n", "change priority", uid, err); + } + + ret = CMD_OK; + } + + return ret; +} + +static int thcreat_cmd(int argc, char **argv, unsigned int *vRet) +{ + const char *name; + unsigned int entry; + int pri; + int stack; + unsigned int attr; + SceUID uid; + int ret; + + name = argv[0]; + if(!memDecode(argv[1], &entry)) + { + SHELL_PRINT("Invalid entry address %s\n", argv[1]); + return CMD_ERROR; + } + + pri = strtoul(argv[2], NULL, 0); + stack = strtoul(argv[3], NULL, 0); + attr = strtoul(argv[4], NULL, 0); + + uid = sceKernelCreateThread(name, (void*) entry, pri, stack, attr, NULL); + if(uid < 0) + { + SHELL_PRINT("Could not create thread 0x%08X\n", uid); + return CMD_ERROR; + } + + ret = sceKernelStartThread(uid, 0, NULL); + if(ret < 0) + { + SHELL_PRINT("Could not start thread %s 0x%08X\n", name, uid); + return CMD_ERROR; + } + + SHELL_PRINT("Created/Started thread %s UID:0x%08X\n", name, uid); + *vRet = uid; + + return CMD_OK; +} + +static int print_eventinfo(SceUID uid, int verbose) +{ + SceKernelEventFlagInfo info; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferEventFlagStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("Attr: 0x%08X - initPattern 0x%08X - currPatten 0x%08X\n", info.attr, info.initPattern, + info.currentPattern); + SHELL_PRINT("NumWaitThreads: 0x%08X\n", info.numWaitThreads); + } + } + + return ret; +} + +static int evlist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_EventFlag, "EventFlag", print_eventinfo); +} + +static int evinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "EventFlag", print_eventinfo, (ReferFunc) pspSdkReferEventFlagStatusByName); +} + +static int evdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferEventFlagStatusByName, sceKernelDeleteEventFlag); +} + +static int evset_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + unsigned int bits; + int ret; + + uid = get_thread_uid(argv[0], (ReferFunc) pspSdkReferEventFlagStatusByName); + if(uid < 0) + { + return CMD_ERROR; + } + + bits = strtoul(argv[1], NULL, 0); + ret = sceKernelSetEventFlag(uid, bits); + if(ret < 0) + { + SHELL_PRINT("Error setting EventFlag - 0x%08X\n", ret); + } + + return CMD_OK; +} + +static int evclr_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + unsigned int bits; + int ret; + + uid = get_thread_uid(argv[0], (ReferFunc) pspSdkReferEventFlagStatusByName); + if(uid < 0) + { + return CMD_ERROR; + } + + bits = strtoul(argv[1], NULL, 0); + ret = sceKernelClearEventFlag(uid, ~bits); + if(ret < 0) + { + SHELL_PRINT("Error clearing EventFlag - 0x%08X\n", ret); + } + + return CMD_OK; +} + +static int print_semainfo(SceUID uid, int verbose) +{ + SceKernelSemaInfo info; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferSemaStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("Attr: 0x%08X - initCount: 0x%08X - currCount: 0x%08X\n", info.attr, info.initCount, + info.currentCount); + SHELL_PRINT("maxCount: 0x%08X - NumWaitThreads: 0x%08X\n", info.maxCount, info.numWaitThreads); + } + } + + return ret; +} + +static int smlist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_Semaphore, "Semaphore", print_semainfo); +} + +static int sminfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "Semaphore", print_semainfo, (ReferFunc) pspSdkReferSemaStatusByName); +} + +static int smdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferSemaStatusByName, sceKernelDeleteSema); +} + +static int print_mboxinfo(SceUID uid, int verbose) +{ + SceKernelMbxInfo info; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferMbxStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("Attr: 0x%08X - numWaitThreads: 0x%08X - numMessages: 0x%08X\n", info.attr, info.numWaitThreads, + info.numMessages); + SHELL_PRINT("firstMessage 0x%p\n", info.firstMessage); + } + } + + return ret; +} + +static int mxlist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_Mbox, "Message Box", print_mboxinfo); +} + +static int mxinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "Message Box", print_mboxinfo, (ReferFunc) pspSdkReferMboxStatusByName); +} + +static int mxdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferMboxStatusByName, sceKernelDeleteMbx); +} + +static int print_cbinfo(SceUID uid, int verbose) +{ + SceKernelCallbackInfo info; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferCallbackStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("threadId 0x%08X - callback 0x%p - common 0x%p\n", info.threadId, info.callback, info.common); + SHELL_PRINT("notifyCount %d - notifyArg %d\n", info.notifyCount, info.notifyArg); + } + } + + return ret; +} + +static int cblist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_Callback, "Callback", print_cbinfo); +} + +static int cbinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "Callback", print_cbinfo, (ReferFunc) pspSdkReferCallbackStatusByName); +} + +static int cbdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferCallbackStatusByName, sceKernelDeleteCallback); +} + +static int print_vtinfo(SceUID uid, int verbose) +{ + SceKernelVTimerInfo info; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferVTimerStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("active %d - base.hi %d - base.low %d - current.hi %d - current.low %d\n", + info.active, info.base.hi, info.base.low, info.current.hi, info.current.low); + SHELL_PRINT("schedule.hi %d - schedule.low %d - handler 0x%p - common 0x%p\n", info.schedule.hi, + info.schedule.low, info.handler, info.common); + } + } + + return ret; +} + +static int vtlist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_VTimer, "VTimer", print_vtinfo); +} + +static int vtinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "VTimer", print_vtinfo, (ReferFunc) pspSdkReferVTimerStatusByName); +} + +static int vtdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferVTimerStatusByName, sceKernelDeleteVTimer); +} + +static int print_vplinfo(SceUID uid, int verbose) +{ + SceKernelVplInfo info; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferVplStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("Attr 0x%08X - poolSize %d - freeSize %d - numWaitThreads %d\n", + info.attr, info.poolSize, info.freeSize, info.numWaitThreads); + } + } + + return ret; +} + +static int vpllist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_Vpl, "Vpl", print_vplinfo); +} + +static int vplinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "Vpl", print_vplinfo, (ReferFunc) pspSdkReferVplStatusByName); +} + +static int vpldel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferVplStatusByName, sceKernelDeleteVpl); +} + +static int print_fplinfo(SceUID uid, int verbose) +{ + SceKernelFplInfo info; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferFplStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("Attr 0x%08X - blockSize %d - numBlocks %d - freeBlocks %d - numWaitThreads %d\n", + info.attr, info.blockSize, info.numBlocks, info.freeBlocks, info.numWaitThreads); + } + } + + return ret; +} + +static int fpllist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_Fpl, "Fpl", print_fplinfo); +} + +static int fplinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "Fpl", print_fplinfo, (ReferFunc) pspSdkReferFplStatusByName); +} + +static int fpldel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferFplStatusByName, sceKernelDeleteFpl); +} + +static int print_mppinfo(SceUID uid, int verbose) +{ + SceKernelMppInfo info; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferMsgPipeStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("Attr 0x%08X - bufSize %d - freeSize %d\n", info.attr, info.bufSize, info.freeSize); + SHELL_PRINT("numSendWaitThreads %d - numReceiveWaitThreads %d\n", info.numSendWaitThreads, + info.numReceiveWaitThreads); + } + } + + return ret; +} + +static int mpplist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_Mpipe, "Message Pipe", print_mppinfo); +} + +static int mppinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "Message Pipe", print_mppinfo, (ReferFunc) pspSdkReferMppStatusByName); +} + +static int mppdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferMppStatusByName, sceKernelDeleteMsgPipe); +} + +static int print_thevinfo(SceUID uid, int verbose) +{ + SceKernelThreadEventHandlerInfo info; + int ret; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + ret = sceKernelReferThreadEventHandlerStatus(uid, &info); + if(ret == 0) + { + SHELL_PRINT("UID: 0x%08X - Name: %s\n", uid, info.name); + if(verbose) + { + SHELL_PRINT("threadId 0x%08X - mask %02X - handler 0x%p\n", info.threadId, info.mask, info.handler); + SHELL_PRINT("common 0x%p\n", info.common); + } + } + + return ret; +} + +static int thevlist_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmanlist_cmd(argc, argv, SCE_KERNEL_TMID_ThreadEventHandler, "Thread Event Handler", print_thevinfo); +} + +static int thevinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + return threadmaninfo_cmd(argc, argv, "Thread Event Handler", print_thevinfo, (ReferFunc) pspSdkReferThreadEventHandlerStatusByName); +} + +static int thevdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "delete", (ReferFunc) pspSdkReferThreadEventHandlerStatusByName, sceKernelReleaseThreadEventHandler); +} + +int thread_event_handler(int mask, SceUID thid, void *common) +{ + const char *event = "Unknown"; + const char *thname = "Unknown"; + SceKernelThreadInfo thinfo; + + switch(mask) + { + case THREAD_CREATE: event = "Create"; + break; + case THREAD_START: event = "Start"; + break; + case THREAD_EXIT: event = "Exit"; + break; + case THREAD_DELETE: event = "Delete"; + break; + default: break; + }; + + memset(&thinfo, 0, sizeof(thinfo)); + thinfo.size = sizeof(thinfo); + if(sceKernelReferThreadStatus(thid, &thinfo) == 0) + { + thname = thinfo.name; + } + + SHELL_PRINT("Thread %-6s: thid 0x%08X name %s\n", event, thid, thname); + + return 0; +} + +static int thmon_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID ev; + SceUID uid; + int mask = 0; + + if(g_context.thevent >= 0) + { + sceKernelReleaseThreadEventHandler(g_context.thevent); + g_context.thevent = -1; + } + + switch(argv[0][0]) + { + case 'a': uid = THREADEVENT_ALL; + break; + case 'u': uid = THREADEVENT_USER; + break; + case 'k': uid = THREADEVENT_KERN; + break; + default: return CMD_ERROR; + }; + + if(argc > 1) + { + int loop; + + for(loop = 0; argv[1][loop]; loop++) + { + switch(argv[1][loop]) + { + case 'c': mask |= THREAD_CREATE; + break; + case 's': mask |= THREAD_START; + break; + case 'e': mask |= THREAD_EXIT; + break; + case 'd': mask |= THREAD_DELETE; + break; + default: /* Do nothing */ + break; + }; + } + } + else + { + mask = THREAD_CREATE | THREAD_START | THREAD_EXIT | THREAD_DELETE; + } + + ev = sceKernelRegisterThreadEventHandler("PSPLINK_THEV", uid, mask, thread_event_handler, NULL); + g_context.thevent = ev; + + return CMD_OK; +} + +static int thmonoff_cmd(int argc, char **argv, unsigned int *vRet) +{ + if(g_context.thevent >= 0) + { + sceKernelReleaseThreadEventHandler(g_context.thevent); + g_context.thevent = -1; + } + + return CMD_OK; +} + +static int sysstat_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceKernelSystemStatus stat; + + memset(&stat, 0, sizeof(stat)); + stat.size = sizeof(stat); + + if(!sceKernelReferSystemStatus(&stat)) + { + SHELL_PRINT("System Status: 0x%08X\n", stat.status); + SHELL_PRINT("Idle Clocks: %08X%08X\n", stat.idleClocks.hi, stat.idleClocks.low); + SHELL_PRINT("Resume Count: %d\n", stat.comesOutOfIdleCount); + SHELL_PRINT("Thread Switch: %d\n", stat.threadSwitchCount); + SHELL_PRINT("VFPU Switch: %d\n", stat.vfpuSwitchCount); + } + + return CMD_OK; +} + +static int uidlist_cmd(int argc, char **argv, unsigned int *vRet) +{ + const char *name = NULL; + + if(argc > 0) + { + name = argv[0]; + } + printUIDList(name); + + return CMD_OK; +} + +static int uidinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + uidControlBlock *entry; + const char *parent = NULL; + + if(argc > 1) + { + parent = argv[1]; + } + + if(argv[0][0] == '@') + { + entry = findObjectByNameWithParent(&argv[0][1], parent); + } + else + { + SceUID uid; + + uid = strtoul(argv[0], NULL, 0); + entry = findObjectByUIDWithParent(uid, parent); + } + + if(entry) + { + printUIDEntry(entry); + if(entry->type) + { + SHELL_PRINT("Parent:\n"); + printUIDEntry(entry->type); + } + } + + return CMD_OK; +} + +static int cop0_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int regs[64]; + int i; + + psplinkGetCop0(regs); + + SHELL_PRINT("MXC0 Regs:\n"); + for(i = 0; i < 32; i += 2) + { + SHELL_PRINT("$%02d: 0x%08X - $%02d: 0x%08X\n", i, regs[i], i+1, regs[i+1]); + } + SHELL_PRINT("\n"); + + SHELL_PRINT("CXC0 Regs:\n"); + for(i = 0; i < 32; i += 2) + { + SHELL_PRINT("$%02d: 0x%08X - $%02d: 0x%08X\n", i, regs[i+32], i+1, regs[i+33]); + } + + return CMD_OK; +} + +static void print_modthids(SceUID uid, int verbose, const char *name, int type, int (*print)(SceUID, int)) +{ + SceUID thids[100]; + int count; + + count = psplinkReferThreadsByModule(type, uid, thids, 100); + if(count > 0) + { + int i; + SHELL_PRINT("Module %s (%d)\n", name, count); + for(i = 0; i < count; i++) + { + (void) print(thids[i], verbose); + SHELL_PRINT("\n"); + } + } +} + +static void print_modthreads(SceUID uid, int verbose) +{ + print_modthids(uid, verbose, "Thread", SCE_KERNEL_TMID_Thread, print_threadinfo); +} + +static void print_modcallbacks(SceUID uid, int verbose) +{ + print_modthids(uid, verbose, "Callback", SCE_KERNEL_TMID_Callback, print_cbinfo); +} + +static int print_modinfo(SceUID uid, int verbose, const char *opts) +{ + SceKernelModuleInfo info; + int ret; + + enable_kprintf(0); + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + + ret = g_QueryModuleInfo(uid, &info); + if(ret >= 0) + { + int i; + SHELL_PRINT("UID: 0x%08X Attr: %04X - Name: %s\n", uid, info.attribute, info.name); + if(verbose) + { + SHELL_PRINT("Entry: 0x%08X - GP: 0x%08X - TextAddr: 0x%08X\n", info.entry_addr, + info.gp_value, info.text_addr); + SHELL_PRINT("TextSize: 0x%08X - DataSize: 0x%08X BssSize: 0x%08X\n", info.text_size, + info.data_size, info.bss_size); + for(i = 0; (i < info.nsegment) && (i < 4); i++) + { + SHELL_PRINT("Segment %d: Addr 0x%08X - Size 0x%08X\n", i, + (unsigned int) info.segmentaddr[i], (unsigned int) info.segmentsize[i]); + } + SHELL_PRINT("\n"); + } + + while(*opts) + { + switch(*opts) + { + case 't': /* Print threads for this module */ + print_modthreads(uid, verbose); + break; + case 'c': /* Print callbacks for this module */ + print_modcallbacks(uid, verbose); + break; + default: break; + }; + opts++; + } + } + else + { + SHELL_PRINT("Error querying module 0x%08X\n", ret); + } + + enable_kprintf(1); + + return ret; +} + +static int modinfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + const char *opts = ""; + + uid = get_module_uid(argv[0]); + + if(uid >= 0) + { + if(argc > 1) + { + opts = argv[1]; + } + + if(print_modinfo(uid, 1, opts) < 0) + { + SHELL_PRINT("ERROR: Unknown module 0x%08X\n", uid); + } + else + { + ret = CMD_OK; + } + } + else + { + SHELL_PRINT("ERROR: Invalid module %s\n", argv[0]); + } + + return ret; +} + +static int modlist_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID ids[100]; + int ret; + int count; + int i; + int verbose = 0; + const char *opts = ""; + + if(argc > 0) + { + opts = argv[0]; + while(*opts) + { + if(*opts == 'v') + { + verbose = 1; + break; + } + opts++; + } + opts = argv[0]; + } + + memset(ids, 0, 100 * sizeof(SceUID)); + ret = g_GetModuleIdList(ids, 100 * sizeof(SceUID), &count); + if(ret >= 0) + { + SHELL_PRINT("\n", count); + for(i = 0; i < count; i++) + { + print_modinfo(ids[i], verbose, opts); + } + } + + return CMD_OK; +} + +static int modstop_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + SceUID uid_ret; + int status; + + uid_ret = sceKernelStopModule(uid, 0, NULL, &status, NULL); + SHELL_PRINT("Module Stop 0x%08X Status 0x%08X\n", uid_ret, status); + + ret = CMD_OK; + } + else + { + SHELL_PRINT("ERROR: Invalid argument %s\n", argv[0]); + } + + return ret; +} + +static int modunld_cmd(int argc, char **argv, unsigned int *vRet) +{ + + SceUID uid; + int ret = CMD_ERROR; + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + SceUID uid_ret; + + uid_ret = sceKernelUnloadModule(uid); + SHELL_PRINT("Module Unload 0x%08X\n", uid_ret); + + ret = CMD_OK; + } + else + { + SHELL_PRINT("ERROR: Invalid argument %s\n", argv[0]); + } + + return ret; + +} + +static int modstun_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + SceUID stop, unld; + int status; + + stop = sceKernelStopModule(uid, 0, NULL, &status, NULL); + unld = sceKernelUnloadModule(uid); + SHELL_PRINT("Module Stop/Unload 0x%08X/0x%08X Status 0x%08X\n", stop, unld, status); + + ret = CMD_OK; + } + else + { + SHELL_PRINT("ERROR: Invalid argument %s\n", argv[0]); + } + + return ret; +} + +static int modstart_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + char args[1024]; + int len; + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + SceUID uid_ret; + int status; + + if(argc > 1) + { + len = build_args(args, argv[1], argc - 2, &argv[2]); + } + else + { + len = build_args(args, "unknown", 0, NULL); + } + + uid_ret = sceKernelStartModule(uid, len, args, &status, NULL); + SHELL_PRINT("Module Start 0x%08X Status 0x%08X\n", uid_ret, status); + + ret = CMD_OK; + } + else + { + SHELL_PRINT("ERROR: Invalid argument %s\n", argv[0]); + } + + return ret; +} + +static int modexp_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + if(libsPrintEntries(uid)) + { + ret = CMD_OK; + } + else + { + SHELL_PRINT("ERROR: Couldn't find module %s\n", argv[0]); + } + } + else + { + SHELL_PRINT("ERROR: Invalid argument %s\n", argv[0]); + } + + return ret; +} + +static int modimp_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + if(libsPrintImports(uid)) + { + ret = CMD_OK; + } + else + { + SHELL_PRINT("ERROR: Couldn't find module %s\n", argv[0]); + } + } + else + { + SHELL_PRINT("ERROR: Invalid argument %s\n", argv[0]); + } + + return ret; +} + +static int modfindx_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + unsigned int addr = 0; + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + if(argv[2][0] == '@') + { + addr = libsFindExportByName(uid, argv[1], &argv[2][1]); + } + else + { + char *endp; + unsigned int nid; + + nid = strtoul(argv[2], &endp, 16); + if(*endp != 0) + { + SHELL_PRINT("ERROR: Invalid nid %s\n", argv[2]); + } + else + { + addr = libsFindExportByNid(uid, argv[1], nid); + } + } + } + else + { + SHELL_PRINT("ERROR: Invalid argument %s\n", argv[0]); + } + + if(addr != 0) + { + SHELL_PRINT("Library: %s, Exp %s, Addr: 0x%08X\n", argv[1], argv[2], addr); + + ret = CMD_OK; + } + else + { + SHELL_PRINT("Couldn't find module export\n"); + } + + return ret; +} + +static int modfindi_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + unsigned int addr = 0; + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + if(argv[2][0] == '@') + { + addr = libsFindImportAddrByName(uid, argv[1], &argv[2][1]); + } + else + { + char *endp; + unsigned int nid; + + nid = strtoul(argv[2], &endp, 16); + if(*endp != 0) + { + SHELL_PRINT("ERROR: Invalid nid %s\n", argv[2]); + } + else + { + addr = libsFindImportAddrByNid(uid, argv[1], nid); + } + } + } + else + { + SHELL_PRINT("ERROR: Invalid argument %s\n", argv[0]); + } + + if(addr != 0) + { + SHELL_PRINT("Library: %s, Imp %s, Addr: 0x%08X\n", argv[1], argv[2], addr); + + ret = CMD_OK; + } + else + { + SHELL_PRINT("Couldn't find module import\n"); + } + + return ret; +} + +static int apihook_common(int argc, char **argv, int sleep) +{ + SceUID uid; + int ret = CMD_ERROR; + const char *param = ""; + + if(argc > 4) + { + param = argv[4]; + } + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + if(argv[2][0] == '@') + { + if(apiHookGenericByName(uid, argv[1], &argv[2][1], argv[3][0], param, sleep)) + { + ret = CMD_OK; + } + } + else + { + char *endp; + unsigned int nid; + + nid = strtoul(argv[2], &endp, 16); + if(*endp != 0) + { + SHELL_PRINT("ERROR: Invalid nid %s\n", argv[2]); + } + else + { + if(apiHookGenericByNid(uid, argv[1], nid, argv[3][0], param, 0)) + { + ret = CMD_OK; + } + } + } + } + else + { + SHELL_PRINT("ERROR: Invalid argument %s\n", argv[0]); + } + + return ret; +} + +static int apihook_cmd(int argc, char **argv, unsigned int *vRet) +{ + return apihook_common(argc, argv, 0); +} + +static int apihooks_cmd(int argc, char **argv, unsigned int *vRet) +{ + return apihook_common(argc, argv, 1); +} + +static int apihp_cmd(int argc, char **argv, unsigned int *vRet) +{ + apiHookGenericPrint(); + return CMD_OK; +} + +static int apihd_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int id; + + if(strtoint(argv[0], &id)) + { + apiHookGenericDelete(id); + } + else + { + SHELL_PRINT("Invalid ID for delete\n"); + return CMD_ERROR; + } + + return CMD_OK; +} + +static int modload_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID modid; + SceKernelModuleInfo info; + char path[1024]; + + if(handlepath(g_context.currdir, argv[0], path, TYPE_FILE, 1)) + { + modid = sceKernelLoadModule(path, 0, NULL); + if(!psplinkReferModule(modid, &info)) + { + SHELL_PRINT("Module Load '%s' UID: 0x%08X\n", path, modid); + } + else + { + SHELL_PRINT("Module Load '%s' UID: 0x%08X Name: %s\n", path, modid, info.name); + } + *vRet = modid; + } + else + { + SHELL_PRINT("Error invalid file %s\n", path); + } + + return CMD_OK; +} + +static int modexec_cmd(int argc, char **argv, unsigned int *vRet) +{ + char path[1024]; + char args[1024]; + char *file = NULL; + char *key = NULL; + int len; + struct SceKernelLoadExecParam le; + + if(argv[0][0] == '@') + { + key = &argv[0][1]; + if(argc < 2) + { + return CMD_ERROR; + } + file = argv[1]; + } + else + { + file = argv[0]; + } + + if(handlepath(g_context.currdir, file, path, TYPE_FILE, 1)) + { + len = build_args(args, path, argc - 1, &argv[1]); + le.size = sizeof(le); + le.args = len; + le.argp = args; + le.key = key; + + psplinkStop(); + + sceKernelLoadExec(path, &le); + } + + return CMD_OK; +} + +static int ldstart_cmd(int argc, char **argv, unsigned int *vRet) +{ + char path[MAXPATHLEN]; + SceKernelModuleInfo info; + int ret = CMD_ERROR; + + if(argc > 0) + { + SceUID modid; + + if(handlepath(g_context.currdir, argv[0], path, TYPE_FILE, 1)) + { + modid = load_start_module(path, argc-1, &argv[1]); + if(modid >= 0) + { + if(!psplinkReferModule(modid, &info)) + { + SHELL_PRINT("Load/Start %s UID: 0x%08X\n", path, modid); + } + else + { + SHELL_PRINT("Load/Start %s UID: 0x%08X Name: %s\n", path, modid, info.name); + } + } + else + { + SHELL_PRINT("Failed to Load/Start module '%s' Error: 0x%08X\n", path, modid); + } + *vRet = modid; + + ret = CMD_OK; + } + else + { + SHELL_PRINT("Error invalid file %s\n", path); + } + } + + return ret; +} + +static int kill_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + int ret = CMD_ERROR; + + do + { + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + SceUID thids[100]; + int count; + int i; + int status; + int error; + + error = sceKernelStopModule(uid, 0, NULL, &status, NULL); + if(error < 0) + { + SHELL_PRINT("Error could not stop module 0x%08X\n", error); + break; + } + + SHELL_PRINT("Stop status %08X\n", status); + + count = psplinkReferThreadsByModule(SCE_KERNEL_TMID_Thread, uid, thids, 100); + for(i = 0; i < count; i++) + { + sceKernelTerminateDeleteThread(thids[i]); + } + + if(sceKernelUnloadModule(uid) < 0) + { + SHELL_PRINT("Error could not unload module\n"); + break; + } + + ret = CMD_OK; + } + } + while(0); + + return ret; +} + +static int debug_cmd(int argc, char **argv, unsigned int *vRet) +{ + char path[1024]; + int ret = CMD_ERROR; + + if(handlepath(g_context.currdir, argv[0], path, TYPE_FILE, 1)) + { + if(g_context.gdb == 0) + { + argv[0] = path; + load_gdb(g_context.bootpath, argc, argv); + } + else + { + SHELL_PRINT("Error GDB already running, please reset\n"); + } + + ret = CMD_OK; + } + else + { + SHELL_PRINT("Error invalid file %s\n", path); + } + + return ret; +} + +static int calc_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int val; + char disp; + + if(memDecode(argv[0], &val)) + { + if(argc > 1) + { + disp = upcase(argv[1][0]); + } + else + { + disp = 'X'; + } + + switch(disp) + { + case 'D': SHELL_PRINT("%d\n", val); + break; + case 'O': SHELL_PRINT("%o\n", val); + break; + default : + case 'X': SHELL_PRINT("0x%08X\n", val); + break; + }; + *vRet = val; + } + else + { + SHELL_PRINT("Error could not calculate address\n"); + } + + return CMD_OK; +} + +static int reset_cmd(int argc, char **argv, unsigned int *vRet) +{ + if(argc > 0) + { + if(strcmp(argv[0], "game") == 0) + { + g_context.rebootkey = REBOOT_MODE_GAME; + } + else if(strcmp(argv[0], "vsh") == 0) + { + g_context.rebootkey = REBOOT_MODE_VSH; + } + else if(strcmp(argv[0], "updater") == 0) + { + g_context.rebootkey = REBOOT_MODE_UPDATER; + } + } + + psplinkReset(); + + return CMD_OK; +} + +static int list_dir(const char *name) +{ + char buffer[512]; + char *p = buffer; + int dfd; + static SceIoDirent dir; + + dfd = sceIoDopen(name); + if(dfd >= 0) + { + memset(&dir, 0, sizeof(dir)); + while(sceIoDread(dfd, &dir) > 0) + { + int ploop; + p = buffer; + + if(FIO_SO_ISDIR(dir.d_stat.st_attr) || FIO_S_ISDIR(dir.d_stat.st_mode)) + { + *p++ = 'd'; + } + else + { + *p++ = '-'; + } + + for(ploop = 2; ploop >= 0; ploop--) + { + int bits; + + bits = (dir.d_stat.st_mode >> (ploop * 3)) & 0x7; + if(bits & 4) + { + *p++ = 'r'; + } + else + { + *p++ = '-'; + } + + if(bits & 2) + { + *p++ = 'w'; + } + else + { + *p++ = '-'; + } + + if(bits & 1) + { + *p++ = 'x'; + } + else + { + *p++ = '-'; + } + } + + sprintf(p, " %8d ", (int) dir.d_stat.st_size); + p += strlen(p); + sprintf(p, "%02d-%02d-%04d %02d:%02d ", dir.d_stat.st_mtime.day, + dir.d_stat.st_mtime.month, dir.d_stat.st_mtime.year, + dir.d_stat.st_mtime.hour, dir.d_stat.st_mtime.minute); + p += strlen(p); + sprintf(p, "%s", dir.d_name); + SHELL_PRINT("%s\n", buffer); + memset(&dir, 0, sizeof(dir)); + } + + sceIoDclose(dfd); + } + else + { + SHELL_PRINT("Could not open directory '%s'\n", name); + return CMD_ERROR; + } + + return CMD_OK; +} + +static int ls_cmd(int argc, char **argv, unsigned int *vRet) +{ + char path[1024]; + + if(argc == 0) + { + SHELL_PRINT("Listing directory %s\n", g_context.currdir); + list_dir(g_context.currdir); + } + else + { + int loop; + /* Strip whitespace and append a final slash */ + + for(loop = 0; loop < argc; loop++) + { + if(handlepath(g_context.currdir, argv[loop], path, TYPE_DIR, 1)) + { + SHELL_PRINT("Listing directory %s\n", path); + list_dir(path); + } + } + } + + return CMD_OK; +} + +static int chdir_cmd(int argc, char **argv, unsigned int *vRet) +{ + char *dir; + int ret = CMD_ERROR; + char path[1024]; + + /* Get remainder of string */ + dir = argv[0]; + /* Strip whitespace and append a final slash */ + + if(handlepath(g_context.currdir, dir, path, TYPE_DIR, 1) == 0) + { + SHELL_PRINT("'%s' not a valid directory\n", dir); + } + else + { + strcpy(g_context.currdir, path); + SHELL_PRINT_CMD(SHELL_CMD_CWD, "%s", g_context.currdir); + ret = CMD_OK; + } + + return ret; +} + +static int pwd_cmd(int argc, char **argv, unsigned int *vRet) +{ + SHELL_PRINT("%s\n", g_context.currdir); + + return CMD_OK; +} + +static int usbstat_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int state; + + state = sceUsbGetState(); + SHELL_PRINT("USB Status:\n"); + SHELL_PRINT("Connection : %s\n", state & PSP_USB_ACTIVATED ? "activated" : "deactivated"); + SHELL_PRINT("USB Cable : %s\n", state & PSP_USB_CABLE_CONNECTED ? "connected" : "disconnected"); + SHELL_PRINT("USB Connection: %s\n", state & PSP_USB_ACTIVATED ? "established" : "notpresent"); + + return CMD_OK; +} + +static int rename_cmd(int argc, char **argv, unsigned int *vRet) +{ + char asrc[MAXPATHLEN], adst[MAXPATHLEN]; + char *src, *dst; + + src = argv[0]; + dst = argv[1]; + + if( !handlepath(g_context.currdir, src, asrc, TYPE_FILE, 1) ) + return CMD_ERROR; + + if( !handlepath(g_context.currdir, dst, adst, TYPE_FILE, 0) ) + return CMD_ERROR; + + if( sceIoRename(asrc, adst) < 0) + return CMD_ERROR; + + SHELL_PRINT("rename %s -> %s\n", asrc, adst); + + return CMD_OK; +} + +static int rm_cmd(int argc, char **argv, unsigned int *vRet) +{ + char *file, afile[MAXPATHLEN]; + int i; + + for(i = 0; i < argc; i++) + { + file = argv[0]; + + if( !handlepath(g_context.currdir, file, afile, TYPE_FILE, 1) ) + continue; + + if( sceIoRemove(afile) < 0 ) + continue; + + SHELL_PRINT("rm %s\n", afile); + } + + return CMD_OK; +} + +static int mkdir_cmd(int argc, char **argv, unsigned int *vRet) +{ + char *file, afile[MAXPATHLEN]; + + file = argv[0]; + + if( !handlepath(g_context.currdir, file, afile, TYPE_FILE, 0) ) + return CMD_ERROR; + + if( sceIoMkdir(afile, 0777) < 0 ) + return CMD_ERROR; + + SHELL_PRINT("mkdir %s\n", afile); + + return CMD_OK; +} + +static int rmdir_cmd(int argc, char **argv, unsigned int *vRet) +{ + char *file, afile[MAXPATHLEN]; + + file = argv[0]; + + if( !handlepath(g_context.currdir, file, afile, TYPE_FILE, 0) ) + return CMD_ERROR; + + if( sceIoRmdir(afile) < 0 ) + return CMD_ERROR; + + SHELL_PRINT("rmdir %s\n", afile); + + return CMD_OK; +} + +static int cp_cmd(int argc, char **argv, unsigned int *vRet) +{ + int in, out; + int n; + char *source; + char *destination; + char *slash; + + char fsrc[MAXPATHLEN]; + char fdst[MAXPATHLEN]; + char buff[2048]; + + source = argv[0]; + destination = argv[1]; + + if( !handlepath(g_context.currdir, source, fsrc, TYPE_FILE, 1) ) + return CMD_ERROR; + + if( !handlepath(g_context.currdir, destination, fdst, TYPE_ETHER, 0) ) + return CMD_ERROR; + + if(isdir(fdst)) + { + int len; + + len = strlen(fdst); + if((len > 0) && (fdst[len-1] != '/')) + { + strcat(fdst, "/"); + } + + slash = strrchr(fsrc, '/'); + strcat(fdst, slash+1); + } + + SHELL_PRINT("cp %s -> %s\n", fsrc, fdst); + + in = sceIoOpen(fsrc, PSP_O_RDONLY, 0777); + if(in < 0) + { + SHELL_PRINT("Couldn't open source file %s, 0x%08X\n", fsrc, in); + return CMD_ERROR; + } + + out = sceIoOpen(fdst, PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0777); + + if(out < 0) + { + sceIoClose(in); + SHELL_PRINT("Couldn't open destination file %s, 0x%08X\n", fdst, out); + return CMD_ERROR; + } + + while(1) { + n = sceIoRead(in, buff, 2048); + + if(n <= 0) + break; + + sceIoWrite(out, buff, n); + } + + sceIoClose(in); + sceIoClose(out); + + return CMD_OK; +} + +static int remap_cmd(int argc, char **argv, unsigned int *vRet) +{ + int ret; + + sceIoUnassign(argv[1]); + + ret = sceIoAssign(argv[1], argv[0], NULL, IOASSIGN_RDWR, NULL, 0); + if(ret < 0) + { + SHELL_PRINT("Error remapping %s to %s, %08X\n", argv[0], argv[1], ret); + } + + return CMD_OK; +} + +static int meminfo_cmd(int argc, char **argv, unsigned int *vRet) +{ + int i; + int pid = 1; + int max = 11; + + if(argc > 0) + { + pid = atoi(argv[0]); + SHELL_PRINT("pid: %d\n", pid); + if((pid < 1) || (pid > max)) + { + SHELL_PRINT("Error, invalid partition number %d\n", pid); + return CMD_ERROR; + } + max = pid + 1; + } + + SHELL_PRINT("Memory Partitions:\n"); + SHELL_PRINT("N | BASE | SIZE | TOTALFREE | MAXFREE | ATTR |\n"); + SHELL_PRINT("---|------------|----------|-----------|-----------|------|\n"); + for(i = pid; i <= max; i++) + { + SceSize total; + SceSize free; + PspSysmemPartitionInfo info; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + if(sceKernelQueryMemoryPartitionInfo(i, &info) == 0) + { + free = sceKernelPartitionMaxFreeMemSize(i); + total = sceKernelPartitionTotalFreeMemSize(i); + SHELL_PRINT("%-2d | 0x%08X | %8d | %9d | %9d | %04X |\n", + i, info.startaddr, info.memsize, total, free, info.attr); + } + } + + return CMD_OK; +} + +static int memreg_cmd(int argc, char **argv, unsigned int *vRet) +{ + memPrintRegions(); + return CMD_OK; +} + +static int memblocks_cmd(int argc, char **argv, unsigned int *vRet) +{ + if(argc > 0) + { + switch(argv[0][0]) + { + case 'f': sceKernelSysMemDump(); + break; + case 't': sceKernelSysMemDumpTail(); + break; + default: return CMD_ERROR; + + }; + } + else + { + sceKernelSysMemDumpBlock(); + } + + return CMD_OK; +} + +static int malloc_cmd(int argc, char **argv, unsigned int *vRet) +{ + int pid; + const char *name; + int type; + int size; + SceUID uid; + + pid = strtoul(argv[0], NULL, 0); + name = argv[1]; + if(argv[2][0] == 'h') + { + type = PSP_SMEM_High; + } + else + { + type = PSP_SMEM_Low; + } + size = strtoul(argv[3], NULL, 0); + uid = sceKernelAllocPartitionMemory(pid, name, type, size, NULL); + if(uid < 0) + { + SHELL_PRINT("Error allocating memory in pid:%d\n", pid); + return CMD_ERROR; + } + + SHELL_PRINT("Allocated %d bytes in pid:%d (UID:0x%08X, Head:0x%08X)\n", size, pid, + uid, (unsigned int) sceKernelGetBlockHeadAddr(uid)); + *vRet = uid; + + return CMD_OK; +} + +int mfree_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + + uid = strtoul(argv[0], NULL, 0); + if(sceKernelFreePartitionMemory(uid) < 0) + { + SHELL_PRINT("Could not free memory block 0x%08X\n", uid); + } + + return CMD_OK; +} + +int mhead_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID uid; + void *p; + + uid = strtoul(argv[0], NULL, 0); + p = sceKernelGetBlockHeadAddr(uid); + if(p == NULL) + { + SHELL_PRINT("Could not get head of memory block 0x%08X\n", uid); + } + else + { + SHELL_PRINT("Head:0x%08X\n", (unsigned int) p); + *vRet = (unsigned int) p; + } + + return CMD_OK; +} + +/* Maximum memory dump size (per screen) */ +#define MAX_MEMDUMP_SIZE 256 +#define MEMDUMP_TYPE_BYTE 1 +#define MEMDUMP_TYPE_HALF 2 +#define MEMDUMP_TYPE_WORD 3 + +/* Print a row of a memory dump, up to row_size */ +static void print_row(const unsigned int* row, s32 row_size, unsigned int addr, int type) +{ + char buffer[128]; + char *p = buffer; + int i = 0; + + sprintf(p, "%08x - ", addr); + p += strlen(p); + + if(type == MEMDUMP_TYPE_WORD) + { + for(i = 0; i < 16; i+=4) + { + if(i < row_size) + { + sprintf(p, "%02X%02X%02X%02X ", row[i+3], row[i+2], row[i+1], row[i]); + } + else + { + sprintf(p, "-------- "); + } + p += strlen(p); + } + } + else if(type == MEMDUMP_TYPE_HALF) + { + for(i = 0; i < 16; i+=2) + { + if(i < row_size) + { + sprintf(p, "%02X%02X ", row[i+1], row[i]); + } + else + { + sprintf(p, "---- "); + } + + p += strlen(p); + } + } + else + { + for(i = 0; i < 16; i++) + { + if(i < row_size) + { + sprintf(p, "%02X ", row[i]); + } + else + { + sprintf(p, "-- "); + } + + p += strlen(p); + } + } + + sprintf(p, "- "); + p += strlen(p); + + for(i = 0; i < 16; i++) + { + if(i < row_size) + { + if((row[i] >= 32) && (row[i] < 127)) + { + *p++ = row[i]; + } + else + { + *p++ = '.'; + } + } + else + { + *p++ = '.'; + } + } + *p = 0; + + SHELL_PRINT("%s\n", buffer); +} + +/* Print a memory dump to SIO */ +static void print_memdump(unsigned int addr, s32 size, int type) +{ + int size_left; + unsigned int row[16]; + int row_size; + u8 *p_addr = (u8 *) addr; + + if(type == MEMDUMP_TYPE_WORD) + { + SHELL_PRINT(" - 00 04 08 0c - 0123456789abcdef\n"); + SHELL_PRINT("-----------------------------------------------------------------\n"); + } + else if(type == MEMDUMP_TYPE_HALF) + { + SHELL_PRINT(" - 00 02 04 06 08 0a 0c 0e - 0123456789abcdef\n"); + SHELL_PRINT("---------------------------------------------------------------------\n"); + } + else + { + SHELL_PRINT(" - 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f - 0123456789abcdef\n"); + SHELL_PRINT("-----------------------------------------------------------------------------\n"); + } + + size_left = size > MAX_MEMDUMP_SIZE ? MAX_MEMDUMP_SIZE : size; + row_size = 0; + + while(size_left > 0) + { + row[row_size] = p_addr[row_size]; + row_size++; + if(row_size == 16) + { + // draw row + print_row(row, row_size, (unsigned int) p_addr, type); + p_addr += 16; + row_size = 0; + } + + size_left--; + } +} + +static int memdump_cmd(int argc, char **argv, unsigned int *vRet) +{ + static unsigned int addr = 0; + static int type = MEMDUMP_TYPE_BYTE; + s32 size_left; + + /* Get memory address */ + if(argc > 0) + { + if(argv[0][0] == '-') + { + addr -= MAX_MEMDUMP_SIZE; + } + else + { + if(!memDecode(argv[0], &addr)) + { + SHELL_PRINT("Error, invalid memory address %s\n", argv[0]); + return CMD_ERROR; + } + } + + if(argc > 1) + { + if(argv[1][0] == 'w') + { + type = MEMDUMP_TYPE_WORD; + } + else if(argv[1][0] == 'h') + { + type = MEMDUMP_TYPE_HALF; + } + else if(argv[1][0] == 'b') + { + type = MEMDUMP_TYPE_BYTE; + } + } + } + else if(addr == 0) + { + return CMD_ERROR; + } + else + { + addr += MAX_MEMDUMP_SIZE; + } + + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + + if(size_left > 0) + { + print_memdump(addr, size_left, type); + } + else + { + SHELL_PRINT("Invalid memory address 0x%08X\n", addr); + } + + return CMD_OK; +} + +static int savemem_cmd(int argc, char **argv, unsigned int *vRet) +{ + char path[1024]; + unsigned int addr; + int size; + int written; + char *endp; + + if(!handlepath(g_context.currdir, argv[2], path, TYPE_FILE, 0)) + { + SHELL_PRINT("Error invalid path\n"); + return CMD_ERROR; + } + + size = strtoul(argv[1], &endp, 0); + if(*endp != 0) + { + SHELL_PRINT("Size parameter invalid '%s'\n", argv[1]); + return CMD_ERROR; + } + + if(memDecode(argv[0], &addr)) + { + int size_left; + int fd; + + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + size = size > size_left ? size_left : size; + fd = sceIoOpen(path, PSP_O_CREAT | PSP_O_TRUNC | PSP_O_WRONLY, 0777); + if(fd < 0) + { + SHELL_PRINT("Could not open file '%s' for writing 0x%08X\n", path, fd); + } + else + { + written = 0; + while(written < size) + { + int ret; + + ret = sceIoWrite(fd, (void *) (addr + written), size - written); + if(ret <= 0) + { + SHELL_PRINT("Could not write out file\n"); + break; + } + + written += ret; + } + sceIoClose(fd); + } + } + else + { + return CMD_ERROR; + } + + return CMD_OK; +} + +static int loadmem_cmd(int argc, char **argv, unsigned int *vRet) +{ + char path[1024]; + unsigned int addr; + int maxsize = -1; + char *endp; + + if(!handlepath(g_context.currdir, argv[1], path, TYPE_FILE, 1)) + { + SHELL_PRINT("Error invalid path\n"); + return CMD_ERROR; + } + + if(argc > 2) + { + maxsize = strtoul(argv[2], &endp, 0); + if(*endp != 0) + { + SHELL_PRINT("Size parameter invalid '%s'\n", argv[2]); + return CMD_ERROR; + } + } + + if(memDecode(argv[0], &addr)) + { + int size_left; + int readbytes; + int fd; + + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + fd = sceIoOpen(path, PSP_O_RDONLY, 0777); + if(fd < 0) + { + SHELL_PRINT("Could not open file '%s' for reading 0x%08X\n", path, fd); + } + else + { + int size = 0; + + if(maxsize >= 0) + { + size = maxsize > size_left ? size_left : maxsize; + } + else + { + size = size_left; + } + + readbytes = 0; + while(readbytes < size) + { + int ret; + + ret = sceIoRead(fd, (void *) (addr + readbytes), size - readbytes); + if(ret < 0) + { + SHELL_PRINT("Could not write out file\n"); + break; + } + else if(ret == 0) + { + break; + } + + readbytes += ret; + } + sceIoClose(fd); + SHELL_PRINT("Read %d bytes into memory\n", readbytes); + } + } + else + { + return CMD_ERROR; + } + + return CMD_OK; +} + +static int pokew_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + + if(memDecode(argv[0], &addr)) + { + int size_left; + int i; + + addr &= ~3; + size_left = memValidate(addr, MEM_ATTRIB_WRITE | MEM_ATTRIB_WORD); + if(size_left >= sizeof(unsigned int)) + { + for(i = 1; i < argc; i++) + { + unsigned int data; + + if(memDecode(argv[i], &data)) + { + _sw(data, addr); + } + else + { + SHELL_PRINT("Invalid value %s\n", argv[i]); + } + + addr += 4; + size_left -= 4; + if(size_left <= 0) + { + break; + } + } + } + else + { + SHELL_PRINT("Invalid memory address 0x%08X\n", addr); + return CMD_ERROR; + } + } + + return CMD_OK; +} + +static int pokeh_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + + if(memDecode(argv[0], &addr)) + { + int size_left; + int i; + + addr &= ~1; + size_left = memValidate(addr, MEM_ATTRIB_WRITE | MEM_ATTRIB_HALF); + if(size_left >= sizeof(u16)) + { + for(i = 1; i < argc; i++) + { + unsigned int data; + + if(memDecode(argv[i], &data)) + { + _sh(data, addr); + } + else + { + SHELL_PRINT("Invalid value %s\n", argv[i]); + } + + addr += 2; + size_left -= 2; + if(size_left <= 0) + { + break; + } + } + } + else + { + SHELL_PRINT("Invalid memory address 0x%08X\n", addr); + return CMD_ERROR; + } + } + + return CMD_OK; +} + +static int pokeb_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + + if(memDecode(argv[0], &addr)) + { + int size_left; + int i; + + size_left = memValidate(addr, MEM_ATTRIB_WRITE | MEM_ATTRIB_BYTE); + if(size_left > 0) + { + for(i = 1; i < argc; i++) + { + unsigned int data; + + if(memDecode(argv[i], &data)) + { + _sb(data, addr); + } + else + { + SHELL_PRINT("Invalid value %s\n", argv[i]); + } + + addr += 1; + size_left -= 1; + if(size_left <= 0) + { + break; + } + } + } + else + { + SHELL_PRINT("Invalid memory address 0x%08X\n", addr); + return CMD_ERROR; + } + } + + return CMD_OK; +} + +static int pokes_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + + if(memDecode(argv[0], &addr)) + { + int size_left; + int str_len; + + size_left = memValidate(addr, MEM_ATTRIB_WRITE | MEM_ATTRIB_WORD); + str_len = strlen(argv[1]) + 1; + if(size_left >= str_len) + { + strcpy((void*) addr, argv[1]); + } + else + { + SHELL_PRINT("Invalid memory address 0x%08X\n", addr); + return CMD_ERROR; + } + } + + return CMD_OK; +} + +static int peekw_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + + if(memDecode(argv[0], &addr)) + { + int size_left; + char fmt = 'x'; + + if(argc > 1) + { + fmt = argv[1][0]; + } + + addr &= ~3; + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_WORD); + if(size_left >= sizeof(unsigned int)) + { + switch(fmt) + { + case 'f': { + char floatbuf[64]; + float *pdata; + + pspSdkDisableFPUExceptions(); + pdata = (float *) addr; + f_cvt(pdata, floatbuf, sizeof(floatbuf), 6, MODE_GENERIC); + SHELL_PRINT("0x%08X: %s\n", addr, floatbuf); + } + break; + case 'd': SHELL_PRINT("0x%08X: %d\n", addr, _lw(addr)); + break; + case 'o': SHELL_PRINT("0x%08X: %o\n", addr, _lw(addr)); + break; + case 'x': + default: SHELL_PRINT("0x%08X: 0x%08X\n", addr, _lw(addr)); + break; + }; + } + else + { + SHELL_PRINT("Invalid memory address 0x%08X\n", addr); + return CMD_ERROR; + } + } + + return CMD_OK; +} + +static int peekh_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + + if(memDecode(argv[0], &addr)) + { + int size_left; + char fmt = 'x'; + + if(argc > 1) + { + fmt = argv[1][0]; + } + + addr &= ~1; + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_HALF); + if(size_left >= sizeof(u16)) + { + switch(fmt) + { + case 'd': SHELL_PRINT("0x%08X: %d\n", addr, _lh(addr)); + break; + case 'o': SHELL_PRINT("0x%08X: %o\n", addr, _lh(addr)); + break; + case 'x': + default: SHELL_PRINT("0x%08X: 0x%04X\n", addr, _lh(addr)); + break; + }; + } + else + { + SHELL_PRINT("Invalid memory address 0x%08X\n", addr); + return CMD_ERROR; + } + } + + return CMD_OK; +} + +static int peekb_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + + if(memDecode(argv[0], &addr)) + { + int size_left; + char fmt = 'x'; + + if(argc > 1) + { + fmt = argv[1][0]; + } + + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + if(size_left > 0) + { + switch(fmt) + { + case 'd': SHELL_PRINT("0x%08X: %d\n", addr, _lb(addr)); + break; + case 'o': SHELL_PRINT("0x%08X: %o\n", addr, _lb(addr)); + break; + case 'x': + default: SHELL_PRINT("0x%08X: 0x%02X\n", addr, _lb(addr)); + break; + }; + } + else + { + SHELL_PRINT("Invalid memory address 0x%08X\n", addr); + return CMD_ERROR; + } + } + + return CMD_OK; +} + +static int fillb_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + unsigned int size; + + if(memDecode(argv[0], &addr) && memDecode(argv[1], &size)) + { + unsigned int size_left; + unsigned int val; + + size_left = memValidate(addr, MEM_ATTRIB_WRITE | MEM_ATTRIB_BYTE); + size = size > size_left ? size_left : size; + + if(strtoint(argv[2], &val) == 0) + { + SHELL_PRINT("Invalid fill value %s\n", argv[2]); + return CMD_ERROR; + } + + memset((void *) addr, val, size); + } + + return CMD_OK; +} + +static int fillh_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + unsigned int size; + + if(memDecode(argv[0], &addr) && memDecode(argv[1], &size)) + { + unsigned int size_left; + unsigned int val; + int i; + u16 *ptr; + + addr &= ~1; + + size_left = memValidate(addr, MEM_ATTRIB_WRITE | MEM_ATTRIB_HALF); + size = size > size_left ? size_left : size; + + if(strtoint(argv[2], &val) == 0) + { + SHELL_PRINT("Invalid fill value %s\n", argv[2]); + return CMD_ERROR; + } + + ptr = (u16*) addr; + + for(i = 0; i < (size / 2); i++) + { + ptr[i] = (u16) val; + } + } + + return CMD_OK; +} + +static int fillw_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + unsigned int size; + + if(memDecode(argv[0], &addr) && memDecode(argv[1], &size)) + { + unsigned int size_left; + unsigned int val; + int i; + unsigned int *ptr; + + addr &= ~3; + + size_left = memValidate(addr, MEM_ATTRIB_WRITE | MEM_ATTRIB_WORD); + size = size > size_left ? size_left : size; + + if(strtoint(argv[2], &val) == 0) + { + SHELL_PRINT("Invalid fill value %s\n", argv[2]); + return CMD_ERROR; + } + + ptr = (unsigned int*) addr; + + for(i = 0; i < (size / 4); i++) + { + ptr[i] = (unsigned int) val; + } + } + + return CMD_OK; +} + +static int findstr_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + unsigned int size; + + if(memDecode(argv[0], &addr) && memDecode(argv[1], &size)) + { + unsigned int size_left; + int searchlen; + void *curr, *found; + + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + size = size_left > size ? size : size_left; + searchlen = strlen(argv[2]); + curr = (void *) addr; + + do + { + found = memmem_mask(curr, NULL, size, argv[2], searchlen); + if(found) + { + SHELL_PRINT("Found match at address 0x%p\n", found); + found++; + size -= (found - curr); + curr = found; + } + } + while((found) && (size > 0)); + } + + return CMD_OK; +} + +static int findw_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + unsigned int size; + + if(memDecode(argv[0], &addr) && memDecode(argv[1], &size)) + { + unsigned int size_left; + int searchlen; + void *curr, *found; + uint8_t search[128]; + int i; + + searchlen = 0; + for(i = 2; i < argc; i++) + { + unsigned int val; + + if(strtoint(argv[i], &val) == 0) + { + SHELL_PRINT("Invalid search value %s\n", argv[i]); + return CMD_ERROR; + } + + memcpy(&search[searchlen], &val, sizeof(val)); + searchlen += sizeof(val); + } + + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + size = size_left > size ? size : size_left; + curr = (void *) addr; + + do + { + found = memmem_mask(curr, NULL, size, search, searchlen); + if(found) + { + SHELL_PRINT("Found match at address 0x%p\n", found); + found++; + size -= (found - curr); + curr = found; + } + } + while((found) && (size > 0)); + } + + return CMD_OK; +} + +static int findh_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + unsigned int size; + + if(memDecode(argv[0], &addr) && memDecode(argv[1], &size)) + { + unsigned int size_left; + int searchlen; + void *curr, *found; + uint8_t search[128]; + int i; + + searchlen = 0; + for(i = 2; i < argc; i++) + { + unsigned int val; + + if(strtoint(argv[i], &val) == 0) + { + SHELL_PRINT("Invalid search value %s\n", argv[i]); + return CMD_ERROR; + } + + memcpy(&search[searchlen], &val, sizeof(u16)); + searchlen += sizeof(u16); + } + + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + size = size_left > size ? size : size_left; + curr = (void *) addr; + + do + { + found = memmem_mask(curr, NULL, size, search, searchlen); + if(found) + { + SHELL_PRINT("Found match at address 0x%p\n", found); + found++; + size -= (found - curr); + curr = found; + } + } + while((found) && (size > 0)); + } + + return CMD_OK; +} + +static int findhex_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + unsigned int size; + uint8_t hex[128]; + uint8_t *mask = NULL; + uint8_t mask_d[128]; + int hexsize; + int masksize; + + if(memDecode(argv[0], &addr) && memDecode(argv[1], &size)) + { + unsigned int size_left; + void *curr, *found; + + hexsize = decode_hexstr(argv[2], hex, sizeof(hex)); + if(hexsize == 0) + { + SHELL_PRINT("Error in search string\n"); + return CMD_ERROR; + } + + if(argc > 3) + { + masksize = decode_hexstr(argv[3], mask_d, sizeof(mask_d)); + if(masksize == 0) + { + SHELL_PRINT("Error in mask string\n"); + return CMD_ERROR; + } + + if(masksize != hexsize) + { + SHELL_PRINT("Hex and mask do not match\n"); + return CMD_ERROR; + } + + mask = mask_d; + } + + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_BYTE); + size = size_left > size ? size : size_left; + curr = (void *) addr; + + do + { + found = memmem_mask(curr, mask, size, hex, hexsize); + if(found) + { + SHELL_PRINT("Found match at address 0x%p\n", found); + found++; + size -= (found - curr); + curr = found; + } + } + while((found) && (size > 0)); + } + + return CMD_OK; +} + +static int copymem_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int src; + unsigned int dest; + unsigned int size; + + if((memDecode(argv[0], &src)) && (memDecode(argv[1], &dest)) && memDecode(argv[2], &size)) + { + unsigned int size_left; + unsigned int srcsize; + unsigned int destsize; + + srcsize = memValidate(src, MEM_ATTRIB_WRITE | MEM_ATTRIB_BYTE); + destsize = memValidate(dest, MEM_ATTRIB_WRITE | MEM_ATTRIB_BYTE); + size_left = srcsize > destsize ? destsize : srcsize; + size = size > size_left ? size_left : size; + + memmove((void *) dest, (void *) src, size); + } + + return CMD_OK; +} + +static int disasm_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + unsigned int count = 1; + int i; + + if(argc > 1) + { + if(!memDecode(argv[1], &count) || (count == 0)) + { + SHELL_PRINT("Invalid count argument\n"); + return CMD_ERROR; + } + } + + if(memDecode(argv[0], &addr)) + { + int size_left; + + addr &= ~3; + size_left = memValidate(addr, MEM_ATTRIB_READ | MEM_ATTRIB_WORD | MEM_ATTRIB_EXEC); + if((size_left / 4) < count) + { + count = size_left / 4; + } + + for(i = 0; i < count; i++) + { + SHELL_PRINT_CMD(SHELL_CMD_DISASM, "0x%08X:0x%08X", addr, _lw(addr)); + addr += 4; + } + } + + return CMD_OK; +} + +static int memprot_cmd(int argc, char **argv, unsigned int *vRet) +{ + if(strcmp(argv[0], "on") == 0) + { + memSetProtoff(0); + } + else if(strcmp(argv[0], "off") == 0) + { + memSetProtoff(1); + } + else + { + return CMD_ERROR; + } + + return CMD_OK; +} + +static int scrshot_cmd(int argc, char **argv, unsigned int *vRet) +{ + char path[1024]; + SceUID block_id; + void *block_addr; + void *frame_addr; + int frame_width; + int pixel_format; + int sync = 1; + int pri = 2; + unsigned int p; + + if(argc > 1) + { + pri = strtol(argv[1], NULL, 10); + } + + if(!handlepath(g_context.currdir, argv[0], path, TYPE_FILE, 0)) + { + SHELL_PRINT("Error invalid path\n"); + return CMD_ERROR; + } + + if((sceDisplayGetFrameBufferInternal(pri, &frame_addr, &frame_width, &pixel_format, sync) < 0) || (frame_addr == NULL)) + { + SHELL_PRINT("Invalid frame address\n"); + return CMD_ERROR; + } + + block_id = sceKernelAllocPartitionMemory(4, "scrshot", PSP_SMEM_Low, 512*1024, NULL); + if(block_id < 0) + { + SHELL_PRINT("Error could not allocate memory buffer 0x%08X\n", block_id); + return CMD_ERROR; + } + + block_addr = sceKernelGetBlockHeadAddr(block_id); + SHELL_PRINT("frame_addr 0x%p, frame_width %d, pixel_format %d output %s\n", frame_addr, frame_width, pixel_format, path); + + p = (unsigned int) frame_addr; + if(p & 0x80000000) + { + p |= 0xA0000000; + } + else + { + p |= 0x40000000; + } + + bitmapWrite((void *) p, block_addr, pixel_format, path); + + sceKernelFreePartitionMemory(block_id); + + return CMD_OK; +} + +static int dcache_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr = 0; + unsigned int size = 0; + void (*cacheall)(void); + void (*cacherange)(const void *addr, unsigned int size); + + if(argc == 2) + { + SHELL_PRINT("Must specify a size\n"); + return CMD_ERROR; + } + + if(strcmp(argv[0], "w") == 0) + { + cacheall = sceKernelDcacheWritebackAll; + cacherange = sceKernelDcacheWritebackRange; + } + else if(strcmp(argv[0], "i") == 0) + { + cacheall = sceKernelDcacheInvalidateAll; + cacherange = sceKernelDcacheInvalidateRange; + } + else if(strcmp(argv[0], "wi") == 0) + { + cacheall = sceKernelDcacheWritebackInvalidateAll; + cacherange = sceKernelDcacheWritebackInvalidateRange; + } + else + { + SHELL_PRINT("Invalid type specifier '%s'\n", argv[0]); + return CMD_ERROR; + } + + if(argc > 1) + { + if(!memDecode(argv[1], &addr)) + { + SHELL_PRINT("Invalid address\n"); + return CMD_ERROR; + } + + if(!strtoint(argv[2], &size)) + { + SHELL_PRINT("Invalid size argument\n"); + return CMD_ERROR; + } + + cacherange((void *) addr, size); + } + else + { + cacheall(); + } + + return CMD_OK; +} + +static int icache_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr = 0; + unsigned int size = 0; + + if(argc == 1) + { + SHELL_PRINT("Must specify a size\n"); + return CMD_ERROR; + } + + if(argc > 0) + { + if(!memDecode(argv[0], &addr)) + { + SHELL_PRINT("Invalid address\n"); + return CMD_ERROR; + } + + if(!strtoint(argv[1], &size)) + { + SHELL_PRINT("Invalid size argument\n"); + return CMD_ERROR; + } + + sceKernelIcacheInvalidateRange((void *) addr, size); + } + else + { + sceKernelIcacheInvalidateAll(); + } + + return CMD_OK; +} + +static int modaddr_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + SceModule *pMod; + const char *opts = ""; + + if(argc > 1) + { + opts = argv[1]; + } + + if(memDecode(argv[0], &addr)) + { + pMod = sceKernelFindModuleByAddress(addr); + if(pMod != NULL) + { + print_modinfo(pMod->modid, 1, opts); + } + else + { + SHELL_PRINT("Couldn't find module at address 0x%08X\n", addr); + } + } + else + { + SHELL_PRINT("Invalid address %s\n", argv[0]); + return CMD_ERROR; + } + + return CMD_OK; +} + +static int exprint_cmd(int argc, char **argv, unsigned int *vRet) +{ + exceptionPrint(NULL); + + return CMD_OK; +} + +static int exlist_cmd(int argc, char **argv, unsigned int *vRet) +{ + exceptionList(); + + return CMD_OK; +} + +static int exctx_cmd(int argc, char **argv, unsigned int *vRet) +{ + exceptionSetCtx(atoi(argv[0])); + + return CMD_OK; +} + +static int exprfpu_cmd(int argc, char **argv, unsigned int *vRet) +{ + exceptionFpuPrint(NULL); + + return CMD_OK; +} + +static int exprvfpu_cmd(int argc, char **argv, unsigned int *vRet) +{ + int type = VFPU_PRINT_SINGLE; + + if(argc > 0) + { + switch(argv[0][0]) + { + case 's': break; + case 'c': type = VFPU_PRINT_COL; + break; + case 'r': type = VFPU_PRINT_ROW; + break; + case 'm': type = VFPU_PRINT_MATRIX; + break; + case 'e': type = VFPU_PRINT_TRANS; + break; + default: SHELL_PRINT("Unknown format code '%c'\n", argv[0][0]); + return CMD_ERROR; + } + } + + exceptionVfpuPrint(NULL, type); + + return CMD_OK; +} + +static int exresume_cmd(int argc, char **argv, unsigned int *vRet) +{ + if(argc > 0) + { + unsigned int addr; + + if(memDecode(argv[0], &addr)) + { + unsigned int *epc; + + epc = exceptionGetReg("epc"); + if(epc != NULL) + { + *epc = addr; + } + else + { + SHELL_PRINT("Could not get EPC register\n"); + } + } + else + { + return CMD_ERROR; + } + } + + exceptionResume(NULL, PSP_EXCEPTION_CONTINUE); + + return CMD_OK; +} + +static int setreg_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + unsigned int *reg; + + if(memDecode(argv[1], &addr)) + { + if(argv[0][0] != '$') + { + SHELL_PRINT("Error register must start with a $\n"); + return CMD_ERROR; + } + + reg = exceptionGetReg(&argv[0][1]); + if(reg == NULL) + { + SHELL_PRINT("Error could not find register %s\n", argv[0]); + return CMD_ERROR; + } + + *reg = addr; + } + + return CMD_OK; +} + +static int bpth_cmd(int argc, char **argv, unsigned int *vRet) +{ + return thread_do_cmd(argv[0], "break", (ReferFunc) pspSdkReferThreadStatusByName, debugBreakThread); +} + +static int bpset_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int addr; + int ret = CMD_ERROR; + unsigned int flags = 0; + + if(argc > 1) + { + int i = 0; + while(argv[1][i]) + { + if(argv[1][i] == '1') + { + flags |= DEBUG_BP_ONESHOT; + } + else if(argv[1][i] == 'h') + { + flags |= DEBUG_BP_HARDWARE; + } + + i++; + } + } + + if(memDecode(argv[0], &addr)) + { + int size_left; + + addr &= ~3; + size_left = memValidate(addr, MEM_ATTRIB_WRITE | MEM_ATTRIB_WORD | MEM_ATTRIB_EXEC); + if(size_left >= sizeof(unsigned int)) + { + if(debugSetBP(addr, flags, 0)) + { + ret = CMD_OK; + } + else + { + SHELL_PRINT("Could not set breakpoint\n"); + } + } + else + { + SHELL_PRINT("Error, invalidate memory address for breakpoint\n"); + } + } + + return ret; +} + +static int bpdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int id; + struct Breakpoint *bp; + + if(memDecode(argv[0], &id)) + { + bp = debugFindBPByIndex(id); + if(bp) + { + debugDeleteBP(bp->addr); + } + else + { + debugDeleteBP(id); + } + } + else + { + SHELL_PRINT("Invalid ID for delete\n"); + return CMD_ERROR; + } + + return CMD_OK; +} + +static int bpdis_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int id; + struct Breakpoint *bp; + + if(memDecode(argv[0], &id)) + { + bp = debugFindBPByIndex(id); + if(bp) + { + debugDisableBP(bp->addr); + } + else + { + debugDisableBP(id); + } + } + else + { + SHELL_PRINT("Invalid ID for delete\n"); + return CMD_ERROR; + } + + return CMD_OK; +} + +static int bpena_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int id; + struct Breakpoint *bp; + + if(memDecode(argv[0], &id)) + { + bp = debugFindBPByIndex(id); + if(bp) + { + debugEnableBP(bp->addr); + } + else + { + debugEnableBP(id); + } + } + else + { + SHELL_PRINT("Invalid ID for delete\n"); + return CMD_ERROR; + } + + return CMD_OK; +} + +static int bpprint_cmd(int argc, char **argv, unsigned int *vRet) +{ + debugPrintBPs(); + + return CMD_OK; +} + +static int hwprint_cmd(int argc, char **argv, unsigned int *vRet) +{ + debugPrintHWRegs(); + + return CMD_OK; +} + +static int step_cmd(int argc, char **argv, unsigned int *vRet) +{ + exceptionResume(NULL, PSP_EXCEPTION_STEP); + + return CMD_OK; +} + +static int skip_cmd(int argc, char **argv, unsigned int *vRet) +{ + exceptionResume(NULL, PSP_EXCEPTION_SKIP); + + return CMD_OK; +} + +static int version_cmd(int argc, char **argv, unsigned int *vRet) +{ + SHELL_PRINT("PSPLink %s\n", PSPLINK_VERSION); + + return CMD_OK; +} + +static int pspver_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int rev; + + rev = sceKernelDevkitVersion(); + + SHELL_PRINT("Version: %d.%d.%d (0x%08X)\n", (rev >> 24) & 0xFF, (rev >> 16) & 0xFF, (rev >> 8) & 0xFF, rev); + + return CMD_OK; +} + +static int config_cmd(int argc, char **argv, unsigned int *vRet) +{ + configPrint(g_context.bootpath); + + return CMD_OK; +} + +static int confset_cmd(int argc, char **argv, unsigned int *vRet) +{ + if(argc > 1) + { + configChange(g_context.bootpath, argv[0], argv[1], CONFIG_MODE_ADD); + } + else + { + configChange(g_context.bootpath, argv[0], "", CONFIG_MODE_ADD); + } + + return CMD_OK; +} + +static int confdel_cmd(int argc, char **argv, unsigned int *vRet) +{ + configChange(g_context.bootpath, argv[0], "", CONFIG_MODE_DEL); + + return CMD_OK; +} + +static int power_cmd(int argc, char **argv, unsigned int *vRet) +{ + int batteryLifeTime = 0; + char fbuf[128]; + + SHELL_PRINT("External Power: %s\n", scePowerIsPowerOnline()? "yes" : "no "); + SHELL_PRINT("%-14s: %s\n", "Battery", scePowerIsBatteryExist()? "present" : "absent "); + + if (scePowerIsBatteryExist()) { + float batvolt; + SHELL_PRINT("%-14s: %s\n", "Low Charge", scePowerIsLowBattery()? "yes" : "no "); + SHELL_PRINT("%-14s: %s\n", "Charging", scePowerIsBatteryCharging()? "yes" : "no "); + batteryLifeTime = scePowerGetBatteryLifeTime(); + batvolt = (float) scePowerGetBatteryVolt() / 1000.0; + SHELL_PRINT("%-14s: %d%% (%02dh%02dm) \n", "Charge", + scePowerGetBatteryLifePercent(), batteryLifeTime/60, batteryLifeTime-(batteryLifeTime/60*60)); + f_cvt(&batvolt, fbuf, sizeof(fbuf), 3, MODE_GENERIC); + SHELL_PRINT("%-14s: %sV\n", "Volts", fbuf); + SHELL_PRINT("%-14s: %d deg C\n", "Battery Temp", scePowerGetBatteryTemp()); + } else + SHELL_PRINT("Battery stats unavailable\n"); + + SHELL_PRINT("%-14s: %d MHz\n", "CPU Speed", scePowerGetCpuClockFrequency()); + SHELL_PRINT("%-14s: %d MHz\n", "Bus Speed", scePowerGetBusClockFrequency()); + + return CMD_OK; +} + +static int poweroff_cmd(int argc, char **argv, unsigned int *vRet) +{ + scePowerRequestStandby(); + + return CMD_OK; +} + +static int clock_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int val1, val2, val3; + + if((strtoint(argv[0], &val1)) && (strtoint(argv[1], &val2)) && (strtoint(argv[2], &val3))) + { + (void) scePowerSetClockFrequency(val1, val2, val3); + } + else + { + SHELL_PRINT("Invalid clock values\n"); + return CMD_ERROR; + } + + return CMD_OK; +} + +static int profmode_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int *debug; + const char *mode; + + debug = get_debug_register(); + if(debug) + { + if(argc > 0) + { + switch(argv[0][0]) + { + case 't': + *debug &= DEBUG_REG_PROFILER_MASK; + *debug |= DEBUG_REG_THREAD_PROFILER; + break; + case 'g': + *debug &= DEBUG_REG_PROFILER_MASK; + *debug |= DEBUG_REG_GLOBAL_PROFILER; + break; + case 'o': + *debug &= DEBUG_REG_PROFILER_MASK; + break; + default: SHELL_PRINT("Invalid profiler mode '%s'\n", argv[0]); + return CMD_ERROR; + }; + SHELL_PRINT("Profiler mode set, you must now reset psplink\n"); + } + else + { + if((*debug & DEBUG_REG_THREAD_PROFILER) == DEBUG_REG_THREAD_PROFILER) + { + mode = "Thread"; + } + else if((*debug & DEBUG_REG_GLOBAL_PROFILER) == DEBUG_REG_GLOBAL_PROFILER) + { + mode = "Global"; + } + else + { + mode = "Off"; + } + + SHELL_PRINT("Profiler Mode: %s\n", mode); + } + } + + + return CMD_OK; +} + +static int debugreg_cmd(int argc, char **argv, unsigned int *vRet) +{ + unsigned int *debug; + + debug = get_debug_register(); + if(debug) + { + if(argc > 0) + { + unsigned int val; + + if(strtoint(argv[0], &val)) + { + *debug = val; + } + else + { + SHELL_PRINT("Invalid debug reg value '%s'\n", argv[0]); + return CMD_ERROR; + } + } + else + { + SHELL_PRINT("Debug Register: 0x%08X\n", *debug); + } + } + + return CMD_OK; +} + +const char* PspInterruptNames[67] = {//67 interrupts + 0, 0, 0, 0, "GPIO", "ATA_ATAPI", "UmdMan", "MScm0", + "Wlan", 0, "Audio", 0, "I2C", 0, "SIRCS_IrDA", + "Systimer0", "Systimer1", "Systimer2", "Systimer3", + "Thread0", "NAND", "DMACPLUS", "DMA0", "DMA1", + "Memlmd", "GE", 0, 0, 0, 0, "Display", "MeCodec", 0, + 0, 0, 0, "HP_Remote", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "MScm1", "MScm2", + 0, 0, 0, "Thread1", "Interrupt" +}; + +static void print_irq(PspIntrHandlerOptionParam *data, int intno, int sub) +{ + const char *name = PspInterruptNames[intno]; + + if(name == NULL) + { + name = "Unknown"; + } + + if(data->entry) + { + if(sub >= 0) + { + SHELL_PRINT("Subintr %d/%d/%s - Calls %d\n", intno, sub, name, data->calls); + } + else + { + SHELL_PRINT("Interrupt %d/%s - Calls %d\n", intno, name, data->calls); + } + SHELL_PRINT("Entry 0x%08X - Common 0x%08X\n", data->entry, data->common); + SHELL_PRINT("GP 0x%08X - Intrcode %d\n", data->gp, data->intr_code); + SHELL_PRINT("SubCount %d - IntrLevel %d\n", data->sub_count, data->intr_level); + SHELL_PRINT("Enabled %d - field_1c 0x%08X\n", data->enabled, data->field_1C); + SHELL_PRINT("totalclk_lo 0x%08X - totalclk_hi 0x%08X\n", data->total_clock_lo, data->total_clock_hi); + SHELL_PRINT("minclk_lo 0x%08X - minclk_hi 0x%08X\n", data->min_clock_lo, data->min_clock_hi); + SHELL_PRINT("maxclk_lo 0x%08X - maxclk_hi 0x%08X\n\n", data->max_clock_lo, data->max_clock_hi); + } +} + +static int irqs_cmd(int argc, char **argv, unsigned int *vRet) +{ + PspIntrHandlerOptionParam data; + int i; + + if(argc > 0) + { + int intno; + int ret; + + intno = strtoul(argv[0], NULL, 0); + memset(&data, 0, sizeof(data)); + data.size = sizeof(data); + ret = QueryIntrHandlerInfo(intno, -1, &data); + if(ret == 0) + { + print_irq(&data, intno, -1); + if(data.sub_count > 0) + { + int subs = data.sub_count; + for(i = 0; i < subs; i++) + { + memset(&data, 0, sizeof(data)); + data.size = sizeof(data); + ret = QueryIntrHandlerInfo(intno, i, &data); + if(ret == 0) + { + print_irq(&data, intno, i); + } + else + { + SHELL_PRINT("Arg: %08X\n", ret); + } + } + } + } + } + else + { + for(i = 0; i < 67; i++) + { + int ret; + memset(&data, 0, sizeof(data)); + data.size = sizeof(data); + ret = QueryIntrHandlerInfo(i, -1, &data); + print_irq(&data, i, -1); + } + } + + return CMD_OK; +} + +static int iena_cmd(int argc, char **argv, unsigned int *vRet) +{ + int disable = 0; + int intno = 0; + int subno = -1; + + if(argv[0][0] == 'e') + { + disable = 0; + } + else if(argv[0][0] == 'd') + { + disable = 1; + } + else + { + return CMD_ERROR; + } + + intno = strtoul(argv[1], NULL, 0); + if(argc > 2) + { + subno = strtoul(argv[2], NULL, 0); + } + + if(subno < 0) + { + if(disable) + { + sceKernelDisableIntr(intno); + } + else + { + sceKernelEnableIntr(intno); + } + } + else + { + if(disable) + { + sceKernelDisableSubIntr(intno, subno); + } + else + { + sceKernelEnableSubIntr(intno, subno); + } + } + + return CMD_OK; +} + +static int irel_cmd(int argc, char **argv, unsigned int *vRet) +{ + int intno = 0; + int subno = -1; + + intno = strtoul(argv[0], NULL, 0); + if(argc > 1) + { + subno = strtoul(argv[1], NULL, 0); + } + if(subno < 0) + { + sceKernelReleaseIntrHandler(intno); + } + else + { + sceKernelReleaseSubIntrHandler(intno, subno); + } + + return CMD_OK; +} + +static int tonid_cmd(int argc, char **argv, unsigned int *vRet) +{ + SHELL_PRINT("Name: %s, Nid: 0x%08X\n", argv[0], libsNameToNid(argv[0])); + + return CMD_OK; +} + +int call_thread(SceSize args, void *argp) +{ + if(args == sizeof(struct call_frame)) + { + struct call_frame *s = (struct call_frame *) argp; + u64 ret; + + ret = s->func(s->args[0], s->args[1], s->args[2], s->args[3], s->args[4], s->args[5]); + SHELL_PRINT("Return: 0x%08X:0x%08X\n", ret >> 32, ret & 0xFFFFFFFF); + } + + sceKernelExitDeleteThread(0); + + return 0; +} + +static int call_cmd(int argc, char **argv, unsigned int *vRet) +{ + struct call_frame frame; + SceUID uid; + SceUInt timeout; + + memset(&frame, 0, sizeof(frame)); + if(memDecode(argv[0], (void*) &frame.func)) + { + int i; + for(i = 1; i < argc; i++) + { + if(!memDecode(argv[i], &frame.args[i-1])) + { + break; + } + } + if(i != argc) + { + SHELL_PRINT("Invalid parameter %s\n", argv[i]); + return CMD_ERROR; + } + + SHELL_PRINT("Func 0x%p ", frame.func); + for(i = 0; i < argc-1; i++) + { + SHELL_PRINT("arg%d 0x%08x ", i, frame.args[i]); + } + SHELL_PRINT("\n"); + + uid = sceKernelCreateThread("CallThread", call_thread, 0x18, 0x1000, 0, NULL); + if(uid >= 0) + { + timeout = 1000000; + sceKernelStartThread(uid, sizeof(frame), &frame); + sceKernelWaitThreadEnd(uid, &timeout); + } + } + else + { + SHELL_PRINT("Invalid function address %s\n", argv[0]); + return CMD_ERROR; + } + + return CMD_OK; +} + +static void tab_do_uid(int argc, char **argv) +{ + SceUID ids[100]; + const char *name; + int ret; + int count; + int i; + int namelen; + name = &argv[0][1]; + namelen = strlen(name); + + enable_kprintf(0); + memset(ids, 0, 100 * sizeof(SceUID)); + ret = g_GetModuleIdList(ids, 100 * sizeof(SceUID), &count); + if(ret >= 0) + { + SceKernelModuleInfo info; + + for(i = 0; i < count; i++) + { + if(psplinkReferModule(ids[i], &info) == 0) + { + if(strncmp(info.name, name, namelen) == 0) + { + SHELL_PRINT_CMD(SHELL_CMD_TAB, "@%s", info.name); + } + } + } + } + enable_kprintf(1); +} + +static void tab_do_dir(int argc, char **argv) +{ + char *dir; + char path[1024]; + + dir = argv[0]; + if(handlepath(g_context.currdir, dir, path, TYPE_DIR, 1) == 0) + { + /* We dont care about sending an error, shouldn't even print */ + } + else + { + int did; + int namelen; + const char *name; + SceIoDirent entry; + + if(argc > 1) + { + name = argv[1]; + } + else + { + name = ""; + } + namelen = strlen(name); + did = sceIoDopen(path); + if(did >= 0) + { + while(1) + { + memset(&entry, 0, sizeof(entry)); + if(sceIoDread(did, &entry) <= 0) + { + break; + } + /* We eliminate . and .. */ + if(strcmp(entry.d_name, ".") && strcmp(entry.d_name, "..")) + { + if(strncmp(entry.d_name, name, namelen) == 0) + { + if(FIO_SO_ISDIR(entry.d_stat.st_attr) || FIO_S_ISDIR(entry.d_stat.st_mode)) + { + SHELL_PRINT_CMD(SHELL_CMD_TAB, "%s/", entry.d_name); + } + else + { + SHELL_PRINT_CMD(SHELL_CMD_TAB, "%s", entry.d_name); + } + } + } + } + sceIoDclose(did); + } + } +} + +static int tab_cmd(int argc, char **argv, unsigned int *vRet) +{ + if(argv[0][0] == '@') + { + tab_do_uid(argc, argv); + } + else + { + tab_do_dir(argc, argv); + } + + return CMD_OK; +} + +static void print_sym_info(SceUID uid) +{ + SceKernelModuleInfo info; + int ret; + + enable_kprintf(0); + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + + ret = g_QueryModuleInfo(uid, &info); + if(ret >= 0) + { + SHELL_PRINT_CMD(SHELL_CMD_SYMLOAD, "%08X%08X%s", info.text_addr, sceKernelDevkitVersion(), info.name); + } + enable_kprintf(1); + +} + +static int symload_cmd(int argc, char **argv, unsigned int *vRet) +{ + SceUID ids[100]; + SceUID uid; + int ret; + int count; + int i; + + if(argc > 0) + { + + uid = get_module_uid(argv[0]); + if(uid >= 0) + { + print_sym_info(uid); + } + else + { + return CMD_ERROR; + } + } + else + { + memset(ids, 0, 100 * sizeof(SceUID)); + ret = g_GetModuleIdList(ids, 100 * sizeof(SceUID), &count); + if(ret >= 0) + { + for(i = 0; i < count; i++) + { + print_sym_info(ids[i]); + } + } + } + + return CMD_OK; +} + +static int exit_cmd(int argc, char **argv, unsigned int *vRet) +{ + return CMD_EXITSHELL; +} + +/* Define the list of commands */ +const struct sh_command commands[] = { + SHELL_COMMANDS +}; + +/* Find a command from the command list */ +static const struct sh_command* find_command(const char *cmd) +{ + const struct sh_command* found_cmd = NULL; + int cmd_loop; + + for(cmd_loop = 0; commands[cmd_loop].name != NULL; cmd_loop++) + { + if(strcmp(cmd, commands[cmd_loop].name) == 0) + { + found_cmd = &commands[cmd_loop]; + break; + } + + if(commands[cmd_loop].syn) + { + if(strcmp(cmd, commands[cmd_loop].syn) == 0) + { + found_cmd = &commands[cmd_loop]; + break; + } + } + } + + return found_cmd; +} + +int shellExecute(int argc, char **argv, unsigned int *vRet) +{ + int ret = CMD_OK; + char *cmd; + + scePowerTick(0); + + if((argc > 0) && (argv[0][0] != '#')) + { + const struct sh_command *found_cmd; + + cmd = argv[0]; + /* Check for a completion function */ + found_cmd = find_command(cmd); + if((found_cmd) && (found_cmd->func) && (found_cmd->min_args <= (argc-1))) + { + ret = found_cmd->func(argc-1, &argv[1], vRet); + } + else + { + ret = CMD_ERROR; + } + } + + return ret; +} + +int shellParseThread(SceSize args, void *argp) +{ + int ret; + unsigned char cli[MAX_CLI]; + int argc; + char *argv[16]; + unsigned int vRet; + + usbShellInit(); + + while(1) + { + argc = usbShellReadInput(cli, argv, MAX_CLI, 16); + if(argc > 0) + { + vRet = 0; + if(setjmp(g_context.parseenv) == 0) + { + ret = shellExecute(argc, argv, &vRet); + } + else + { + ret = CMD_ERROR; + } + + if(ret == CMD_OK) + { + SHELL_PRINT_CMD(SHELL_CMD_SUCCESS, "0x%08X", vRet); + } + else if(ret == CMD_ERROR) + { + SHELL_PRINT_CMD(SHELL_CMD_ERROR, "0x%08X", vRet); + } + else if(ret == CMD_EXITSHELL) + { + psplinkExitShell(); + } + } + } + + return 0; +} + +int shellInit(const char *init_dir) +{ + strcpy(g_context.currdir, init_dir); + + return 0; +} diff --git a/psplink/shell.h b/psplink/shell.h new file mode 100644 index 0000000..e033b61 --- /dev/null +++ b/psplink/shell.h @@ -0,0 +1,24 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * shell.h - PSPLINK kernel module shell code + * + * Copyright (c) 2005 James F + * Copyright (c) 2005 Julian T + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/shell.h $ + * $Id: shell.h 2037 2006-10-22 17:05:29Z tyranid $ + */ + +/* Return values for the commands */ +#define CMD_EXITSHELL 1 +#define CMD_OK 0 +#define CMD_ERROR -1 + +/* Parse a command string */ +int shellParse(char *command); +void shellStart(void); +int shellInit(const char *init_dir); +int shellParseThread(SceSize args, void *argp); diff --git a/psplink/shellcmd.h b/psplink/shellcmd.h new file mode 100644 index 0000000..9f5aace --- /dev/null +++ b/psplink/shellcmd.h @@ -0,0 +1,288 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * shellcmd.h - PSPLINK shell command definitions + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/psplink/psplink.h $ + * $Id: psplink.h 2021 2006-10-12 22:22:21Z tyranid $ + */ + +#ifndef __SHELLCMD_H +#define __SHELLCMD_H + +#define SHELL_CMD_BEGIN 0xFF +#define SHELL_CMD_END 0xFE +#define SHELL_CMD_SUCCESS 0xFD +#define SHELL_CMD_ERROR 0xFC +#define SHELL_CMD_CWD 0xFB +#define SHELL_CMD_TAB 0xFA +#define SHELL_CMD_LASTMOD 0xF9 +#define SHELL_CMD_DISASM 0xF8 +#define SHELL_CMD_SYMLOAD 0xF7 + +#ifdef _PCTERM +/* Structure to hold a single command entry */ +struct sh_command +{ + const char *name; /* Normal name of the command */ + const char *syn; /* Synonym of the command */ + int (*func)(int argc, char **argv); + int min_args; + const char *desc; /* Textual description */ + const char *detail; + const char *help; /* Command usage */ +}; + +#define SHELL_CAT(name, desc) \ + { name, NULL, NULL, 0, desc, "", NULL }, +#define SHELL_CMD(name, syn, func, min_args, desc, detail, help) \ + { name, syn, NULL, min_args, desc, detail, help }, +#define SHELL_CMD_SHARED(name, syn, func, min_args, desc, detail, help) \ + { name, syn, func, min_args, desc, detail, help }, +#define SHELL_CMD_PCTERM SHELL_CMD_SHARED +#define SHELL_CMD_PSP(name, syn, func, min_args, desc, detail, help) + +#else +/* Structure to hold a single command entry */ +struct sh_command +{ + const char *name; /* Normal name of the command */ + const char *syn; /* Synonym of the command */ + int (*func)(int argc, char **argv, unsigned int *vRet); /* Pointer to the command function */ + int min_args; +}; + +/* Help category, empty on psp side */ +#define SHELL_CAT(name, desc) +/* Normal command */ +#define SHELL_CMD(name, syn, func, min_args, desc, detail, help) \ + { name, syn, func, min_args }, +/* Command that is used on both PSP and PCTERM side (to do special processing) */ +#define SHELL_CMD_SHARED SHELL_CMD +/* Command that is only used on the PCTERM */ +#define SHELL_CMD_PCTERM(name, syn, func, min_args, desc, detail, help) +/* Command that is only used on the PSP */ +#define SHELL_CMD_PSP SHELL_CMD + +#endif + +/* Define all the shell commands so it can be shared between psplink and pcterm */ +#define SHELL_COMMANDS \ + SHELL_CAT("thread", "Commands to manipulate threads") \ + SHELL_CMD("thlist", "tl", thlist_cmd, 0, "List the threads in the system.", \ + "If v is specified as an argument the output will be verbose.", "[v]") \ + SHELL_CMD("thsllist", NULL, thsllist_cmd, 0, "List the sleeping threads in the system.", \ + "If v is specified as an argument the output will be verbose.", "[v]") \ + SHELL_CMD("thdelist", NULL, thdelist_cmd, 0, "List the delayed threads in the system", \ + "If v is specified as an argument the output will be verbose.", "[v]") \ + SHELL_CMD("thsulist", NULL, thsulist_cmd, 0, "List the suspended threads in the system", \ + "If v is specified as an argument the output will be verbose.", "[v]") \ + SHELL_CMD("thdolist", NULL, thdolist_cmd, 0, "List the dormant threads in the system", \ + "If v is specified as an argument the output will be verbose.", "[v]") \ + SHELL_CMD("thinfo", "ti", thinfo_cmd, 1, "Print info about a thread", \ + "Prints more detailed information about a thread. uid specifies a numeric unique identifier, "\ + "@name is the name of the thread.", "uid|@name") \ + SHELL_CMD("thsusp", "ts", thsusp_cmd, 1, "Suspend a thread", \ + "Suspends a thread (so it stops executing). To resume the thread use the `thresm` command. " \ + "uid specifies a numeric unique identifier, @name is the name of the thread.", "uid|@name") \ + SHELL_CMD("thspuser", NULL, thspuser_cmd, 0, "Suspend all user threads", "", "") \ + SHELL_CMD("thresm", "tr", thresm_cmd, 1, "Resume a thread", "", "uid|@name") \ + SHELL_CMD("thwake", "tw", thwake_cmd, 1, "Wakeup a thread", "", "uid|@name") \ + SHELL_CMD("thterm", "tt", thterm_cmd, 1, "Terminate a thread", "", "uid|@name") \ + SHELL_CMD("thdel", "td", thdel_cmd, 1, "Delete a thread", "", "uid|@name") \ + SHELL_CMD("thtdel", "tx", thtdel_cmd, 1, "Terminate and delete a thread", "", "uid|@name") \ + SHELL_CMD("thctx", NULL, thctx_cmd, 1, "Find and print the full thread context", "", "uid|@name") \ + SHELL_CMD("thpri", "tp", thpri_cmd, 2, "Change a threads current priority", "", "uid|@name pri") \ + SHELL_CMD("thcreat", "tc", thcreat_cmd, 5, "Create a new thread", "", "name entry pri stack attr" )\ + SHELL_CMD("evlist", "el", evlist_cmd, 0, "List the event flags in the system", "", "[v]") \ + SHELL_CMD("evinfo", "ei", evinfo_cmd, 1, "Print info about an event flag", "", "uid|@name") \ + SHELL_CMD("evdel", "ed", evdel_cmd, 1, "Delete an event flag", "", "uid|@name") \ + SHELL_CMD("evset", "es", evset_cmd, 2, "Set event flag bits", "", "uid|@name pattern") \ + SHELL_CMD("evclr", "ec", evclr_cmd, 2, "Clear event flag bits", "", "uid|@name pattern") \ + SHELL_CMD("smlist", "sl", smlist_cmd, 0, "List the semaphores in the system", "", "[v]") \ + SHELL_CMD("sminfo", "si", sminfo_cmd, 1, "Print info about a semaphore", "", "uid|@name") \ + SHELL_CMD("smdel", "sd", smdel_cmd, 1, "Delete a semaphore", "", "uid|@name") \ + SHELL_CMD("mxlist", "xl", mxlist_cmd, 0, "List the message boxes in the system", "", "[v]") \ + SHELL_CMD("mxinfo", "xi", mxinfo_cmd, 1, "Print info about a message box", "", "uid|@name") \ + SHELL_CMD("mxdel", "xd", mxdel_cmd, 1, "Delete a messagebox", "", "uid|@name") \ + SHELL_CMD("cblist", "cl", cblist_cmd, 0, "List the callbacks in the system", "", "[v]") \ + SHELL_CMD("cbinfo", "ci", cbinfo_cmd, 1, "Print info about a callback", "", "uid|@name") \ + SHELL_CMD("cbdel", "cbd", cbdel_cmd, 1, "Delete a callback", "", "uid|@name") \ + SHELL_CMD("vtlist", "zl", vtlist_cmd, 0, "List the virtual timers in the system", "", "[v]") \ + SHELL_CMD("vtinfo", "zi", vtinfo_cmd, 1, "Print info about a virtual timer", "", "uid|@name") \ + SHELL_CMD("vtdel", "zd", vtdel_cmd, 1, "Delete a virtual timer", "", "uid|@name") \ + SHELL_CMD("vpllist","vl", vpllist_cmd, 0, "List the variable pools in the system", "", "[v]") \ + SHELL_CMD("vplinfo","vi", vplinfo_cmd, 1, "Print info about a variable pool", "", "uid|@name") \ + SHELL_CMD("vpldel", "vd", vpldel_cmd, 1, "Delete a variable pool", "", "uid|@name") \ + SHELL_CMD("fpllist","fl", fpllist_cmd, 0, "List the fixed pools in the system", "", "[v]") \ + SHELL_CMD("fplinfo","fi", fplinfo_cmd, 1, "Print info about a fixed pool", "", "uid|@name") \ + SHELL_CMD("fpldel", "fd", fpldel_cmd, 1, "Delete a fixed pool", "", "uid|@name") \ + SHELL_CMD("mpplist","pl", mpplist_cmd, 0, "List the message pipes in the system", "", "[v]") \ + SHELL_CMD("mppinfo","pi", mppinfo_cmd, 1, "Print info about a message pipe", "", "uid|@name") \ + SHELL_CMD("mppdel", "pd", mppdel_cmd, 1, "Delete a message pipe", "", "uid|@name") \ + SHELL_CMD("thevlist","tel", thevlist_cmd, 0, "List the thread event handlers in the system", "", "[v]") \ + SHELL_CMD("thevinfo","tei", thevinfo_cmd, 1, "Print info about a thread event handler", "", "uid|@name") \ + SHELL_CMD("thevdel", "ted", thevdel_cmd, 1, "Delete a thread event", "", "uid|@name") \ + SHELL_CMD("thmon", "tm", thmon_cmd, 1, "Monitor thread events", "", "u|k|a [csed]") \ + SHELL_CMD("thmonoff", NULL, thmonoff_cmd, 0, "Disable the thread monitor", "", "") \ + SHELL_CMD("sysstat", NULL, sysstat_cmd, 0, "Print the system status", "", "") \ + \ + SHELL_CAT("module", "Commands to handle modules") \ + SHELL_CMD("modlist","ml", modlist_cmd, 0, "List the currently loaded modules", \ + "[opts] is a string of optional arguments\n" \ + "v - Verbose mode (print all module information)\n" \ + "t - Print the module's threads\n" \ + "c - Print the module's callbacks\n" \ + , "[opts]") \ + SHELL_CMD("modinfo","mi", modinfo_cmd, 1, "Print info about a module", \ + "[opts] is a string of optional arguments\n" \ + "t - Print the module's threads\n" \ + "c - Print the module's callbacks\n" \ + , "uid|@name [opts]") \ + SHELL_CMD("modstop","ms", modstop_cmd, 1, "Stop a running module", "", "uid|@name") \ + SHELL_CMD("modunld","mu", modunld_cmd, 1, "Unload a module (must be stopped)", "", "uid|@name") \ + SHELL_CMD("modstun","mn", modstun_cmd, 1, "Stop and unload a module", "", "uid|@name") \ + SHELL_CMD("modload","md", modload_cmd, 1, "Load a module", "", "path") \ + SHELL_CMD("modstart","mt", modstart_cmd, 1, "Start a module", "", "uid|@name [args]") \ + SHELL_CMD("modexec","me", modexec_cmd, 1, "LoadExec a module", \ + "@key is the exec key, can be @game, @vsh or @updater" \ + , "[@key] path [args]") \ + SHELL_CMD("modaddr","ma", modaddr_cmd, 1, "Display info about the module at a specified address", \ + "[opts] is a string of optional arguments\n" \ + "t - Print the module's threads\n" \ + "c - Print the module's callbacks\n" \ + , "addr [opts]") \ + SHELL_CMD("ldstart","ld", ldstart_cmd, 1, "Load and start a module", "", "path [args]") \ + SHELL_CMD("kill", NULL, kill_cmd, 1, "Kill a module and all it's threads", "", "uid|@name") \ + SHELL_CMD("debug", "d", debug_cmd, 1, "Start a module under GDB", "", "program.elf [args]") \ + SHELL_CMD("modexp", "mp", modexp_cmd, 1, "List the exports from a module", "", "uid|@name") \ + SHELL_CMD("modimp", NULL, modimp_cmd, 1, "List the imports in a module", "", "uid|@name") \ + SHELL_CMD("modfindx", "mfx", modfindx_cmd, 3, "Find a module's export address", "", "uid|@name library nid|@name") \ + SHELL_CMD("modfindi", "mfi", modfindi_cmd, 3, "Find a module's import address", "", "uid|@name library nid|@name") \ + SHELL_CMD("apihook", NULL, apihook_cmd, 4, "Hook a user mode API call", "", "uid|@name library nid|@name ret [param]") \ + SHELL_CMD("apihooks", NULL, apihooks_cmd, 4, "Hook a user mode API call with sleep", "", "uid|@name library nid|@name ret [param]") \ + SHELL_CMD("apihp", NULL, apihp_cmd, 0, "Print the user mode API hooks", "", "") \ + SHELL_CMD("apihd", NULL, apihd_cmd, 1, "Delete an user mode API hook", "", "id") \ + \ + SHELL_CAT("memory", "Commands to manipulate memory") \ + SHELL_CMD("meminfo", "mf", meminfo_cmd, 0, "Print free memory info", "", "[partitionid]") \ + SHELL_CMD("memreg", "mr", memreg_cmd, 0, "Print available memory regions (for other commands)", "", "") \ + SHELL_CMD("memdump", "dm", memdump_cmd, 0, "Dump memory to screen", "", "[addr|-] [b|h|w]") \ + SHELL_CMD("memblocks", "mk", memblocks_cmd, 0, "Dump the sysmem block table", "", "[f|t]") \ + SHELL_CMD("malloc", NULL, malloc_cmd, 4, "Alloc from a mem partition", "", "pid name l|h size") \ + SHELL_CMD("mfree", NULL, mfree_cmd, 1, "Free from a mem partition", "", "uid") \ + SHELL_CMD("mhead", NULL, mhead_cmd, 1, "Get head pointer for a mem allocation", "", "uid") \ + SHELL_CMD("savemem", "sm", savemem_cmd, 3, "Save memory to a file", "", "addr size path") \ + SHELL_CMD("loadmem", "lm", loadmem_cmd, 2, "Load memory from a file", "", "addr path [maxsize]") \ + SHELL_CMD("pokew", "pw", pokew_cmd, 2, "Poke words into memory", "", "addr val1 [val2..valN]") \ + SHELL_CMD("pokeh", "ph", pokeh_cmd, 2, "Poke half words into memory", "", "addr val1 [val2..valN]") \ + SHELL_CMD("pokeb", "pb", pokeb_cmd, 2, "Poke bytes into memory", "", "addr val1 [val2..valN]") \ + SHELL_CMD("pokes", "ps", pokes_cmd, 2, "Poke a string into memory", "", "addr str") \ + SHELL_CMD("peekw", "kw", peekw_cmd, 1, "Peek the word at address", "", "addr [o|b|x|f]") \ + SHELL_CMD("peekh", "kh", peekh_cmd, 1, "Peek the half word at address", "", "addr [o|b|x]") \ + SHELL_CMD("peekb", "kb", peekb_cmd, 1, "Peek the byte at address", "", "addr [o|b|x]") \ + SHELL_CMD("fillw", "fw", fillw_cmd, 3, "Fill a block of memory with a word value", "", "addr size val") \ + SHELL_CMD("fillh", "fh", fillh_cmd, 3, "Fill a block of memory with a half value", "", "addr size val") \ + SHELL_CMD("fillb", "fb", fillb_cmd, 3, "Fill a block of memory with a byte value", "", "addr size val") \ + SHELL_CMD("copymem", "cm", copymem_cmd, 3, "Copy a block of memory", "", "srcaddr destaddr size") \ + SHELL_CMD("findstr", "ns", findstr_cmd, 3, "Find an ASCII string", "", "addr size str") \ + SHELL_CMD("findhex", "nx", findhex_cmd, 3, "Find an hexstring string", "", "addr size hexstr [mask]") \ + SHELL_CMD("findw", "nw", findw_cmd, 3, "Find a list of words", "", "addr size val1 [val2..valN]") \ + SHELL_CMD("findh", "nh", findh_cmd, 3, "Find a list of half words", "", "addr size val1 [val2..valN]") \ + SHELL_CMD("dcache", "dc", dcache_cmd, 1, "Perform a data cache operation", \ + "w - Writeback the cache\n" \ + "i - Invalidate the cache\n" \ + "wi - Writeback and invalidate the cache\n" \ + "addr and size is an optional range. If not specified will perform\n" \ + "the action over the entire cache" \ + , "w|i|wi [addr size]") \ + SHELL_CMD("icache", "ic", icache_cmd, 0, "Invalidate the instruction cache", "", "[addr size]") \ + SHELL_CMD("disasm", "di", disasm_cmd, 1, "Disassemble instructions", "", "address [count]") \ + SHELL_CMD_PCTERM("asm", NULL, asm_cmd, 1, "Assemble instructions to a memory address", "", "addr [inst]") \ + SHELL_CMD_PCTERM("disopts", NULL, disopts_cmd, 0, "Print/set/clear the current disassembler options", "", "[+opts|-opts]") \ + SHELL_CMD("memprot", NULL, memprot_cmd, 1, "Set memory protection on or off", "", "on|off") \ + \ + SHELL_CAT("fileio", "Commands to handle file io") \ + SHELL_CMD("ls", "dir", ls_cmd, 0, "List the files in a directory", "", "[path1..pathN]") \ + SHELL_CMD("chdir", "cd", chdir_cmd, 1, "Change the current directory", "", "path") \ + SHELL_CMD("cp", "copy", cp_cmd, 2, "Copy a file", "", "source destination") \ + SHELL_CMD("mkdir", NULL, mkdir_cmd, 1, "Make a Directory", "", "dir") \ + SHELL_CMD("rm", "del", rm_cmd, 1, "Removes a File", "", "file") \ + SHELL_CMD("rmdir", "rd", rmdir_cmd, 1, "Removes a Directory", "", "dir") \ + SHELL_CMD("rename", "ren", rename_cmd, 2, "Renames a File", "", "src dst") \ + SHELL_CMD("remap", NULL, remap_cmd, 2, "Remaps a device to another", "", "devfrom: devto:") \ + SHELL_CMD("pwd", NULL, pwd_cmd, 0, "Print the current working directory", "", "") \ + \ + SHELL_CAT("debugger", "Debug commands") \ + SHELL_CMD("exprint", "ep", exprint_cmd, 0, "Print the current exception info", "", "[ex]") \ + SHELL_CMD("exlist", "exl", exlist_cmd, 0, "List the exception contexts", "", "") \ + SHELL_CMD("exctx", "ecx", exctx_cmd, 1, "Set the current exception context", "", "ex") \ + SHELL_CMD("exresume", "c", exresume_cmd, 0, "Resume from the exception", "", "[addr]") \ + SHELL_CMD("exprfpu", "ef", exprfpu_cmd, 0, "Print the current FPU registers", "", "[ex]") \ + SHELL_CMD("exprvfpu", "ev", exprvfpu_cmd, 0, "Print the current VFPU registers", "", "[s|c|r|m|e] [ex]") \ + SHELL_CMD("setreg", "str", setreg_cmd, 2, "Set the value of an exception register", "", "$reg value") \ + SHELL_CMD("bpset", "bp", bpset_cmd, 1, "Set a break point", \ + "addr is the address at which the breakpoint should be set.\nOpts is a list of options for the "\ + "breakpoint.\n1 - specifies that this will be a one shot breakpoint\n" \ + "h - specifies this is a hardware breakpoint (if available)\n", \ + "addr [opts]") \ + SHELL_CMD("bpdel", "bc", bpdel_cmd, 1, "Delete a breakpoint", "", "num|addr") \ + SHELL_CMD("bpdis", "bd", bpdis_cmd, 1, "Disable a breakpoint", "", "num|addr") \ + SHELL_CMD("bpena", "be", bpena_cmd, 1, "Enable a breakpoint", "", "num|addr") \ + SHELL_CMD("bpth", NULL, bpth_cmd, 1, "Break a thread at the first available point", "", "uid|@name") \ + SHELL_CMD("bpprint", "bt", bpprint_cmd, 0, "Print the current breakpoints", "", "") \ + SHELL_CMD("hwprint", NULL, hwprint_cmd, 0, "Print the hardware debugger registers", "", "") \ + SHELL_CMD("step", "s", step_cmd, 0, "Step the next instruction", "", "") \ + SHELL_CMD("skip", "k", skip_cmd, 0, "Skip the next instruction (i.e. jump over jals)", "", "") \ + SHELL_CMD("call", NULL, call_cmd, 1, "Issue a function call", "", "addr [arg0...arg5]") \ + \ + SHELL_CAT("misc", "Miscellaneous commands (e.g. USB, exit)") \ + SHELL_CMD("usbstat", "us", usbstat_cmd, 0, "Display the status of the USB connection", "", "") \ + SHELL_CMD("uidlist","ul", uidlist_cmd, 0, "List the system UIDS", "", "[root]") \ + SHELL_CMD("uidinfo", "ui", uidinfo_cmd, 1, "Print info about a UID", "", "uid|@name [parent]") \ + SHELL_CMD("cop0", "c0", cop0_cmd, 0, "Print the cop0 registers", "", "") \ + SHELL_CMD_SHARED("exit", "quit", exit_cmd, 0, "Exit the shell", "", "") \ + SHELL_CMD_PCTERM("close", NULL, close_cmd, 0, "Close the shell (leave PSP running)", "", "") \ + SHELL_CMD("scrshot", "ss", scrshot_cmd, 1, "Take a screen shot", "", "file [pri]") \ + SHELL_CMD("calc", NULL, calc_cmd, 1, "Do a simple address calculation", "", "addr [d|o|x]") \ + SHELL_CMD("reset", "r", reset_cmd, 0, "Reset", "", "[key]") \ + SHELL_CMD("ver", "v", version_cmd, 0, "Print version of psplink", "", "") \ + SHELL_CMD("pspver", NULL, pspver_cmd, 0, "Print the version of PSP", "", "") \ + SHELL_CMD("config", NULL, config_cmd, 0, "Print the configuration file settings", "", "") \ + SHELL_CMD("confset", NULL, confset_cmd, 1, "Set a configuration value", "", "name [value]") \ + SHELL_CMD("confdel", NULL, confdel_cmd, 1, "Delete a configuration value", "", "name") \ + SHELL_CMD("power", NULL, power_cmd, 0, "Print power information", "", "") \ + SHELL_CMD("poweroff", NULL, poweroff_cmd, 0, "Power off the PSP", "", "") \ + SHELL_CMD("clock", NULL, clock_cmd, 3, "Set the clock frequencies", "", "cpu ram bus") \ + SHELL_CMD_PCTERM("tty", NULL, tty_cmd, 0, "Enter TTY mode.", \ + "In this mode all input goes to stdin on the PSP.\nTo exit tty mode type ~. at " \ + "the start of the line.", "") \ + SHELL_CMD("tonid", NULL, tonid_cmd, 1, "Calculate the NID from a name", "", "name") \ + SHELL_CMD("profmode", NULL, profmode_cmd, 0, "Set or display the current profiler mode", "", "[t|g|o]") \ + SHELL_CMD("debugreg", NULL, debugreg_cmd, 0, "Set or display the current debug register", "", "[val]") \ + SHELL_CMD("irqs", NULL, irqs_cmd, 0, "Display the current IRQ list (or a specific int plus subs)", "", "[intno]")\ + SHELL_CMD("iena", NULL, iena_cmd, 2, "Enable or disable an interrupt or sub-interrupt", "", "d|e intno [sub]") \ + SHELL_CMD("irel", NULL, irel_cmd, 1, "Release an interrupt or sub-interrupt", "", "intno [sub]") \ + SHELL_CMD_PCTERM("env", NULL, env_cmd, 0, "Display the environment settings", "", "") \ + SHELL_CMD_PCTERM("set", NULL, set_cmd, 1, "Set an environment variable", "", "name=value") \ + SHELL_CMD_PCTERM("unset", NULL, unset_cmd, 1, "Unset an environment variable", "", "name") \ + SHELL_CMD_PCTERM("echo", NULL, echo_cmd, 0, "Echo text to the screen", "", "[args...]") \ + SHELL_CMD_PCTERM("error", NULL, error_cmd, 1, "Print the name of a known kernel error", "", "no") \ + SHELL_CMD_PCTERM("strlen", NULL, strlen_cmd, 1, "Get length of a string", "", "str") \ + SHELL_CMD_PSP("tab", NULL, tab_cmd, 1, "Tab Completion", "", "dir [file]") \ + SHELL_CMD_SHARED("symload", NULL, symload_cmd, 0, "Load a symbol set", \ + "Load a set of symbols. Can either specify a module name or" \ + "a local path to an ELF file. If no argument is supplied it will" \ + "issue load commands for all current modules. PSP symbol location is " \ + "specified with the PSP_SYMBOL_PATH environment variable.",\ + "symload [@module|file]") \ + SHELL_CMD_PCTERM("help", "?", help_cmd, 0, "Print help about a command", \ + "If a category is specified print all commands underneath. If a command is specified " \ + "prints specific help.", "[category|command]") \ + SHELL_CMD(NULL, NULL, NULL, 0, NULL, NULL, NULL) + +#endif diff --git a/psplink/stdio.c b/psplink/stdio.c new file mode 100644 index 0000000..5edf687 --- /dev/null +++ b/psplink/stdio.c @@ -0,0 +1,189 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * stdio.c - PSPLINK kernel module tty code + * + * Copyright (c) 2005 James F + * Copyright (c) 2005 Julian T + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/stdio.c $ + * $Id: stdio.c 2304 2007-08-26 17:21:11Z tyranid $ + */ + +/* Based off the pspsdk tty code */ + +#include +#include +#include + +static int g_initialised = 0; +static PspDebugInputHandler g_stdin_handler = NULL; +static PspDebugPrintHandler g_stdout_handler = NULL; +static PspDebugPrintHandler g_stderr_handler = NULL; +static PspDebugPrintHandler g_shell_handler = NULL; +static SceUID g_in_sema = 0; +/* Probably stdout and stderr should not be guarded by the same mutex */ +static SceUID g_out_sema = 0; + +extern int sceKernelStdin(void); + + +static int io_init(PspIoDrvArg *arg) +{ + return 0; +} + +static int io_exit(PspIoDrvArg *arg) +{ + return 0; +} + +static int io_open(PspIoDrvFileArg *arg, char *file, int mode, SceMode mask) +{ + if((arg->fs_num != STDIN_FILENO) && (arg->fs_num != STDOUT_FILENO) + && (arg->fs_num != STDERR_FILENO)) + { + return SCE_KERNEL_ERROR_NOFILE; + } + + return 0; +} + +static int io_read(PspIoDrvFileArg *arg, char *data, int len) +{ + int ret = 0; + + (void) sceKernelWaitSema(g_in_sema, 1, 0); + if((arg->fs_num == STDIN_FILENO) && (g_stdin_handler != NULL)) + { + ret = g_stdin_handler(data, len); + } + (void) sceKernelSignalSema(g_in_sema, 1); + + return ret; +} + +static int io_write(PspIoDrvFileArg *arg, const char *data, int len) +{ + int ret = 0; + + (void) sceKernelWaitSema(g_out_sema, 1, 0); + if((arg->fs_num == STDOUT_FILENO) && (g_stdout_handler != NULL)) + { + ret = g_stdout_handler(data, len); + } + else if((arg->fs_num == STDERR_FILENO) && (g_stderr_handler != NULL)) + { + ret = g_stderr_handler(data, len); + } + (void) sceKernelSignalSema(g_out_sema, 1); + + return ret; +} + +static PspIoDrvFuncs tty_funcs = +{ + io_init, + io_exit, + io_open, + NULL, + io_read, + io_write, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static PspIoDrv tty_driver = +{ + "tty", 0x10, 0x800, "TTY", &tty_funcs +}; + +int stdioTtyInit(void) +{ + int ret; + (void) sceIoDelDrv("tty"); /* Ignore error */ + ret = sceIoAddDrv(&tty_driver); + if(ret < 0) + { + return ret; + } + + g_in_sema = sceKernelCreateSema("TtyInMutex", 0, 1, 1, NULL); + if(g_in_sema < 0) + { + return g_in_sema; + } + + g_out_sema = sceKernelCreateSema("TtyOutMutex", 0, 1, 1, NULL); + if(g_out_sema < 0) + { + return g_out_sema; + } + + ret = sceIoReopen("tty0:", PSP_O_RDONLY, 0777, sceKernelStdin()); + if(ret < 0) + { + return ret; + } + + ret = sceKernelStdoutReopen("tty1:", PSP_O_WRONLY, 0777); + if(ret < 0) + { + return ret; + } + + ret = sceKernelStderrReopen("tty2:", PSP_O_WRONLY, 0777); + if(ret < 0) + { + return ret; + } + + g_initialised = 1; + + return 0; +} + +int stdioInstallStdinHandler(PspDebugInputHandler handler) +{ + g_stdin_handler = handler; + + return 0; +} + +int stdioInstallStdoutHandler(PspDebugPrintHandler handler) +{ + g_stdout_handler = handler; + + return 0; +} + +int stdioInstallStderrHandler(PspDebugPrintHandler handler) +{ + g_stderr_handler = handler; + + return 0; +} + +int stdioInstallShellHandler(PspDebugPrintHandler handler) +{ + g_shell_handler = handler; + + return 0; +} + diff --git a/psplink/thctx.c b/psplink/thctx.c new file mode 100644 index 0000000..7eafbb3 --- /dev/null +++ b/psplink/thctx.c @@ -0,0 +1,133 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * thctx.c - Thread context library code for psplink. + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/thctx.c $ + * $Id: thctx.c 2301 2007-08-26 13:48:05Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "psplink.h" +#include "libs.h" +#include "memoryUID.h" +#include "exception.h" + +int threadFindContext(SceUID uid) +{ + SceKernelThreadKInfo info; + struct SceThreadContext ctxCopy; + int found = 0; + int intc; + + memset(&ctxCopy, 0, sizeof(ctxCopy)); + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + + intc = pspSdkDisableInterrupts(); + if(ThreadManForKernel_2D69D086(uid, &info) == 0) + { + found = 1; + if(info.thContext) + { + memcpy(&ctxCopy, info.thContext, sizeof(ctxCopy)); + } + } + pspSdkEnableInterrupts(intc); + + if(found) + { + SHELL_PRINT("kstack 0x%08X kstacksize 0x%08X\n", (unsigned int) info.kstack, info.kstackSize); + SHELL_PRINT("stack 0x%08X stacksize 0x%08X\n", (unsigned int) info.stack, info.stackSize); + SHELL_PRINT("context 0x%08X, vfpu 0x%08X\n", (unsigned int) info.thContext, (unsigned int) info.vfpuContext); + SHELL_PRINT("Context EPC 0x%08X, Real EPC 0x%08X\n", ctxCopy.EPC, info.retAddr); + exceptionPrintCPURegs((unsigned int *) &ctxCopy); + return 0; + } + + return -1; +} + +unsigned int thGetCurrentEPC(SceUID uid) +{ + SceKernelThreadKInfo info; + unsigned int addr = 0; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + if(ThreadManForKernel_2D69D086(uid, &info) == 0) + { + if(info.retAddr) + { + addr = (unsigned int) info.retAddr; + } + else + { + addr = info.thContext->EPC; + } + } + + return addr; +} + +/* Get the thread context of a user thread, trys to infer the real address */ +int psplinkGetFullThreadContext(SceUID uid, struct PsplinkContext *ctx) +{ + SceKernelThreadKInfo info; + int intc; + int ret = 0; + + if(ctx == NULL) + { + return 0; + } + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + + intc = pspSdkDisableInterrupts(); + if(ThreadManForKernel_2D69D086(uid, &info) == 0) + { + memset(ctx, 0, sizeof(struct PsplinkContext)); + ctx->thid = uid; + memcpy(&ctx->regs.r[1], info.thContext->gpr, 31 * sizeof(unsigned int)); + memcpy(&ctx->regs.fpr[0], info.thContext->fpr, 32 * sizeof(float)); + ctx->regs.hi = info.thContext->hi; + ctx->regs.lo = info.thContext->lo; + + if(info.retAddr) + { + ctx->regs.epc = info.scContext->epc; + ctx->regs.status = info.scContext->status; + ctx->regs.frame_ptr = info.scContext->sp; + ctx->regs.r[29] = info.scContext->sp; + ctx->regs.r[31] = info.scContext->ra; + ctx->regs.r[27] = info.scContext->k1; + } + else + { + ctx->regs.epc = info.thContext->EPC; + ctx->regs.status = info.thContext->SR; + ctx->regs.frame_ptr = info.thContext->gpr[28]; + } + + ret = 1; + } + + pspSdkEnableInterrupts(intc); + + return ret; +} diff --git a/psplink/thctx.h b/psplink/thctx.h new file mode 100644 index 0000000..28d1f65 --- /dev/null +++ b/psplink/thctx.h @@ -0,0 +1,22 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * thctx.h - Thread context library code for psplink + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/thctx.h $ + * $Id: thctx.h 2174 2007-02-08 19:49:27Z tyranid $ + */ +#ifndef __THCTX_H__ +#define __THCTX_H__ + +#include "exception.h" + +int threadFindContext(SceUID uid); +unsigned int thGetCurrentEPC(SceUID uid); +int psplinkGetFullThreadContext(SceUID uid, struct PsplinkContext *ctx); + +#endif diff --git a/psplink/tty.c b/psplink/tty.c new file mode 100644 index 0000000..2c88fa1 --- /dev/null +++ b/psplink/tty.c @@ -0,0 +1,108 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * tty.c - PSPLINK kernel module tty code + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/tty.c $ + * $Id: tty.c 2304 2007-08-26 17:21:11Z tyranid $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "psplink.h" +#include "apihook.h" +#include "util.h" +#include "libs.h" +#include "pspstdio.h" + +#define STDIN_BUFSIZE 4096 + +static PspDebugInputHandler g_usbStdinHandler = NULL; +static PspDebugPrintHandler g_usbStdoutHandler = NULL; +static PspDebugPrintHandler g_usbStderrHandler = NULL; + +extern struct GlobalContext g_context; + +static int stdoutHandler(const char *data, int size) +{ + if(g_usbStdoutHandler) + { + g_usbStdoutHandler(data, size); + } + + return size; +} + +static int stderrHandler(const char *data, int size) +{ + if(g_usbStderrHandler) + { + g_usbStderrHandler(data, size); + } + + return size; +} + +static int inputHandler(char *data, int size) +{ + if(g_usbStdinHandler) + { + return g_usbStdinHandler(data, size); + } + + return 0; +} + +void ttySetUsbHandler(PspDebugPrintHandler usbStdoutHandler, PspDebugPrintHandler usbStderrHandler, PspDebugInputHandler usbStdinHandler) +{ + g_usbStdoutHandler = usbStdoutHandler; + g_usbStderrHandler = usbStderrHandler; + g_usbStdinHandler = usbStdinHandler; +} + +static int close_func(int fd) +{ + int ret = SCE_KERNEL_ERROR_FILEERR; + + if(fd > 2) + { + ret = sceIoClose(fd); + } + + return ret; +} + +void ttyInit(void) +{ + SceUID uid; + + if(stdioTtyInit() < 0) + { + Kprintf("Could not initialise tty\n"); + return; + } + + stdioInstallStdoutHandler(stdoutHandler); + stdioInstallStderrHandler(stderrHandler); + stdioInstallStdinHandler(inputHandler); + /* Install a patch to prevent a naughty app from closing stdout */ + uid = refer_module_by_name("sceIOFileManager", NULL); + if(uid >= 0) + { + apiHookByNid(uid, "IoFileMgrForUser", 0x810c4bc3, close_func); + libsPatchFunction(uid, "IoFileMgrForKernel", 0x3c54e908, 0xFFFF); + } +} diff --git a/psplink/tty.h b/psplink/tty.h new file mode 100644 index 0000000..20ff070 --- /dev/null +++ b/psplink/tty.h @@ -0,0 +1,15 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * tty.h - PSPLINK kernel module tty code + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/tty.h $ + * $Id: tty.h 2304 2007-08-26 17:21:11Z tyranid $ + */ + +void ttySetUsbHandler(PspDebugPrintHandler usbStdoutHandler, PspDebugPrintHandler usbStderrHandler, PspDebugInputHandler usbStdinHandler); +void ttyInit(void); diff --git a/psplink/usbshell.c b/psplink/usbshell.c new file mode 100644 index 0000000..08bdab6 --- /dev/null +++ b/psplink/usbshell.c @@ -0,0 +1,164 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK USB Shell main code + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/usbshell/main.c $ + * $Id: main.c 2034 2006-10-18 17:02:37Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tty.h" + +#define MAX_CLI 4096 +#define CTX_BUF_SIZE 128 + +struct prnt_ctx +{ + unsigned short fd; + unsigned short len; + unsigned char buf[CTX_BUF_SIZE]; +}; + +void psplinkPrintPrompt(void); + +struct AsyncEndpoint g_endp; +struct AsyncEndpoint g_stdin; + +int usbStdoutPrint(const char *data, int size) +{ + usbAsyncWrite(ASYNC_STDOUT, data, size); + + return size; +} + +int usbStderrPrint(const char *data, int size) +{ + usbAsyncWrite(ASYNC_STDERR, data, size); + + return size; +} + +int usbStdinRead(char *data, int size) +{ + int ret = 0; + + while(1) + { + ret = usbAsyncRead(ASYNC_STDOUT, (unsigned char*) data, size); + if(ret < 0) + { + sceKernelDelayThread(250000); + continue; + } + + break; + } + + return ret; +} + +int usbShellInit(void) +{ + usbAsyncRegister(ASYNC_SHELL, &g_endp); + usbAsyncRegister(ASYNC_STDOUT, &g_stdin); + ttySetUsbHandler(usbStdoutPrint, usbStderrPrint, usbStdinRead); + usbWaitForConnect(); + psplinkPrintPrompt(); + + return 0; +} + +int usbShellReadInput(unsigned char *cli, char **argv, int max_cli, int max_arg) +{ + int cli_pos = 0; + int argc = 0; + unsigned char *argstart = cli; + + while(1) + { + if(usbAsyncRead(ASYNC_SHELL, &cli[cli_pos], 1) < 1) + { + sceKernelDelayThread(250000); + continue; + } + + if(cli[cli_pos] == 1) + { + cli[cli_pos] = 0; + break; + } + else + { + if(cli_pos < (max_cli-1)) + { + if(cli[cli_pos] == 0) + { + if(argc < max_arg) + { + argv[argc++] = (char*) argstart; + argstart = &cli[cli_pos+1]; + } + } + cli_pos++; + } + } + } + + return argc; +} + +static void cb(struct prnt_ctx *ctx, int type) +{ + if(type == 0x200) + { + ctx->len = 0; + } + else if(type == 0x201) + { + usbAsyncWrite(ASYNC_SHELL, ctx->buf, ctx->len); + ctx->len = 0; + } + else + { + if(type == '\n') + { + cb(ctx, '\r'); + } + + ctx->buf[ctx->len++] = type; + if(ctx->len == CTX_BUF_SIZE) + { + usbAsyncWrite(ASYNC_SHELL, ctx->buf, ctx->len); + ctx->len = 0; + } + } +} + +int shprintf(const char *fmt, ...) +{ + struct prnt_ctx ctx; + va_list opt; + + ctx.len = 0; + + va_start(opt, fmt); + + prnt((prnt_callback) cb, (void*) &ctx, fmt, opt); + + va_end(opt); + + return 0; +} diff --git a/psplink/usbshell.h b/psplink/usbshell.h new file mode 100644 index 0000000..b36ab65 --- /dev/null +++ b/psplink/usbshell.h @@ -0,0 +1,15 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * usbshell.h - PSPLINK kernel module usb shell code + * + * Copyright (c) 2006 James F + * + * $HeadURL$ + * $Id$ + */ + +int usbShellInit(void); +int usbShellReadInput(unsigned char *cli, char **argv, int max_cli, int max_arg); diff --git a/psplink/util.c b/psplink/util.c new file mode 100644 index 0000000..3f393d4 --- /dev/null +++ b/psplink/util.c @@ -0,0 +1,1173 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * util.c - util functions for psplink + * + * Copyright (c) 2005 James F + * Copyright (c) 2005 Julian T + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/util.c $ + * $Id: util.c 2322 2007-09-30 17:49:32Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "psplink.h" +#include "util.h" + +enum UsbStates +{ + USB_NOSTART = 0, + USB_ON = 1, + USB_OFF = 2, +}; + +/* Indicates whether the usb drivers have been loaded */ +static enum UsbStates g_usbhoststate = USB_NOSTART; + +/* Global functions which are setup to point to the correct function + for the firmware */ + +extern struct GlobalContext g_context; +int (*g_QueryModuleInfo)(SceUID modid, SceKernelModuleInfo *info) = NULL; +int (*g_GetModuleIdList)(SceUID *readbuf, int readbufsize, int *idcount) = NULL; + +extern unsigned int sceKernelRemoveByDebugSection; + +int g_isv1 = 0; + +int is_alnum(char ch) +{ + int c; + + c = upcase(ch); + if((c >= 'A') && (c <= 'Z')) + { + return 1; + } + + if((c >= '0') && (c <= '9')) + { + return 1; + } + + return 0; +} + +int is_oct(char ch) +{ + if((ch >= '0') && (ch < '8')) + { + return 1; + } + + return 0; +} + +int oct_to_int(char ch) +{ + if((ch >= '0') && (ch < '8')) + { + return ch - '0'; + } + + return 0; +} + +/* Check if character is a hexadecimal character */ +int is_hex(char ch) +{ + ch = upcase(ch); + + if((ch >= '0') && (ch <= '9')) + return 1; + + if((ch >= 'A') && (ch <= 'F')) + return 1; + + return 0; +} + +/* Convert a single hex digit to an int */ +int hex_to_int(char ch) +{ + if((ch >= '0') && (ch <= '9')) + { + return ch - '0'; + } + + ch = upcase(ch); + if((ch >= 'A') && (ch <= 'F')) + { + return ch - 'A' + 10; + } + + return 0; +} + + +int is_aspace(int ch) +{ + if((ch == ' ') || (ch == '\t') || (ch == '\n') || (ch == '\r')) + { + return 1; + } + + return 0; +} + +/* Normalise the path, remove . and .. directories, will ignore anything at the end with no dir slash */ +static int normalize_path(char *path) +{ + char *last_dir = NULL; + char *curr_pos; + int ret = 1; + + /* Can't start with an absolute path */ + if(*path == '/') + { + ret = 0; + } + else + { + curr_pos = strchr(path, '/'); + while(curr_pos != NULL) + { + if(last_dir != NULL) + { + if(strncmp(last_dir, "/.", curr_pos - last_dir) == 0) + { + strcpy(last_dir, curr_pos); + curr_pos = last_dir; + } + else if(strncmp(last_dir, "/..", curr_pos - last_dir) == 0) + { + char *last_pos; + /* Find the last directory slash from last_dir */ + last_pos = last_dir - 1; + while(last_pos > path) + { + if(*last_pos == '/') + { + break; + } + last_pos--; + } + + if(last_pos > path) + { + last_dir = last_pos; + } + + strcpy(last_dir, curr_pos); + curr_pos = last_dir; + } + else + { + /* Ignore */ + } + } + + last_dir = curr_pos; + curr_pos = strchr(curr_pos + 1, '/'); + } + } + + return ret; +} + +int handlepath(const char *currentdir, const char *relative, char *path, int type, int valid) +{ + int len, fd; + + /* Strip whitespace and append a final slash */ + path[0] = 0; + if(strchr(relative, ':') == NULL) + { + if(relative[0] == '/') + { + int currdir_pos = 0; + int path_pos = 0; + while(currentdir[currdir_pos] != 0) + { + path[path_pos] = currentdir[currdir_pos]; + path_pos++; + if(currentdir[currdir_pos] == ':') + { + break; + } + currdir_pos++; + } + path[path_pos] = 0; + } + else + { + /* relative directory */ + strcpy(path, currentdir); + } + } + + strcat(path, relative); + len = strlen(path); + while((len > 0) && (is_aspace(path[len-1]))) + { + path[len-1] = 0; + len--; + } + + if(len == 0) + { + return 0; + } + + /* Very unsafe, but still */ + if(type == TYPE_DIR && path[len-1] != '/') { + path[len] = '/'; + path[len+1] = 0; + } else if(type == TYPE_FILE && path[len-1] == '/') { + path[len-1] = 0; + } + + if(normalize_path(path) == 0) + return 0; + + if(valid) { + if(type == TYPE_DIR) { + if(!isdir(path)) + { + return 0; + } + } else if(type == TYPE_FILE) { + if((fd = sceIoOpen(path, PSP_O_RDONLY, 0777)) < 0) { + /* Invalid File */ + return 0; + } else { + sceIoClose(fd); + } + } else { + SHELL_PRINT("unable to validate ether type\n"); + return 0; + } + } + + return 1; +} + +/* We do not handle paths relative to current dir (i.e. no . specifiers in the path */ +/* Returns 1 if found and places the result in output */ +/* Not very safe but hey we are not here for that kinda thing :P */ +int findinpath(const char *relative, char *output, const char *pathvar) +{ + int found = 0; + int pathlen; + int fd; + const char *currpath; + const char *nextpath; + + /* Only continue if we have a valid pathvar */ + if(pathvar) + { + currpath = pathvar; + do + { + /* Path separator is semi-colon */ + nextpath = strchr(currpath, ';'); + if(nextpath) + { + memcpy(output, currpath, nextpath-currpath); + output[nextpath-currpath] = 0; + nextpath++; + } + else + { + strcpy(output, currpath); + } + pathlen = strlen(output); + /* Why bother if the path is empty */ + if(pathlen > 0) + { + strcat(output, "/"); + strcat(output, relative); + SHELL_PRINT("%s\n", output); + fd = sceIoOpen(output, PSP_O_RDONLY, 0777); + if(fd >= 0) + { + sceIoClose(fd); + found = 1; + break; + } + } + + currpath = nextpath; + } + while(currpath); + } + + return found; +} + +/* Make the character upper case */ +char upcase(char ch) +{ + if((ch >= 'a') && (ch <= 'z')) + { + ch ^= (1 << 5); + } + + return ch; +} + +int build_bootargs(char *args, const char *bootfile, const char *execfile, int argc, char **argv) +{ + int loc = 0; + int i; + + strcpy(args, bootfile); + loc += strlen(bootfile) + 1; + if(execfile != NULL) + { + strcpy(&args[loc], execfile); + loc += strlen(execfile) + 1; + for(i = 0; i < argc; i++) + { + strcpy(&args[loc], argv[i]); + loc += strlen(argv[i]) + 1; + } + } + + return loc; +} + +int build_args(char *args, const char *execfile, int argc, char **argv) +{ + int loc = 0; + int i; + + strcpy(args, execfile); + loc += strlen(execfile) + 1; + for(i = 0; i < argc; i++) + { + strcpy(&args[loc], argv[i]); + loc += strlen(argv[i]) + 1; + } + + return loc; +} + +int load_start_module(const char *name, int argc, char **argv) +{ + SceUID modid; + int status; + char args[1024]; + int len; + + modid = sceKernelLoadModule(name, 0, NULL); + if(modid >= 0) + { + len = build_args(args, name, argc, argv); + modid = sceKernelStartModule(modid, len, (void *) args, &status, NULL); + } + else + { + Kprintf("lsm: Error loading module %s %08X\n", name, modid); + } + + return modid; +} + +void map_firmwarerev(void) +{ + +#if _PSP_FW_VERSION > 200 + g_QueryModuleInfo = sceKernelQueryModuleInfo; + g_GetModuleIdList = sceKernelGetModuleIdList; + g_isv1 = 0; +#else + unsigned int rev = sceKernelDevkitVersion(); + /* Special case for version 1 firmware */ + if((rev & 0xFFFF0000) == 0x01000000) + { + g_QueryModuleInfo = pspSdkQueryModuleInfoV1; + g_GetModuleIdList = pspSdkGetModuleIdList; + g_isv1 = 1; + } + else + { + g_QueryModuleInfo = sceKernelQueryModuleInfo; + g_GetModuleIdList = sceKernelGetModuleIdList; + g_isv1 = 0; + } +#endif +} + +int sceUsb_E20B23A6(int pid, int power); + +int init_usbhost(const char *bootpath) +{ + int retVal; + char prx_path[MAXPATHLEN]; + + do + { + if((g_usbhoststate == USB_ON)) + { + retVal = 0; + break; + } + + if(g_usbhoststate == USB_NOSTART) + { + strcpy(prx_path, bootpath); + strcat(prx_path, "usbhostfs.prx"); + load_start_module(prx_path, 0, NULL); + } + + retVal = sceUsbStart(PSP_USBBUS_DRIVERNAME, 0, 0); + if (retVal != 0) { + Kprintf("Error starting USB Bus driver (0x%08X)\n", retVal); + break; + } + retVal = sceUsbStart(HOSTFSDRIVER_NAME, 0, 0); + if (retVal != 0) { + Kprintf("Error starting USB Host driver (0x%08X)\n", + retVal); + break; + } + + retVal = sceUsbActivate(g_context.pid); + //retVal = sceUsb_E20B23A6(g_context.pid, 0); + + if(retVal == 0) + { + g_usbhoststate = USB_ON; + } + } + while(0); + + return retVal; +} + +int stop_usbhost(void) +{ + int retVal; + + if((g_usbhoststate != USB_ON)) + { + return 0; + } + + retVal = sceUsbDeactivate(g_context.pid); + if (retVal != 0) { + Kprintf("Error calling sceUsbDeactivate (0x%08X)\n", retVal); + } + + retVal = sceUsbStop(HOSTFSDRIVER_NAME, 0, 0); + if (retVal != 0) { + Kprintf("Error stopping USB host driver (0x%08X)\n", + retVal); + } + + retVal = sceUsbStop(PSP_USBBUS_DRIVERNAME, 0, 0); + if (retVal != 0) { + Kprintf("Error stopping USB BUS driver (0x%08X)\n", retVal); + } + + g_usbhoststate = USB_OFF; + + return 0; +} + +int openfile(const char *filename, PspFile *pFile) +{ + int iRet = 0; + + do + { + if(pFile == NULL) + { + Kprintf("Error, invalid file\n"); + break; + } + + memset(pFile, 0, sizeof(PspFile)); + + pFile->fd = sceIoOpen(filename, PSP_O_RDONLY, 0777); + if(pFile->fd < 0) + { + Kprintf("Error, cannot open file %s\n", filename); + break; + } + + iRet = 1; + } + while(0); + + return iRet; +} + +int closefile(PspFile *pFile) +{ + int iRet = 0; + do + { + if(pFile == NULL) + { + Kprintf("Error, invalid configuration structure\n"); + break; + } + + if(pFile->fd < 0) + { + Kprintf("Error, invalid file descriptor\n"); + break; + } + + sceIoClose(pFile->fd); + iRet = 1; + } + while(0); + + return iRet; +} + +/* Seems the kernel's fdgetc is broken :/ */ +int fdgetc(PspFile *pFile) +{ + int ch = -1; + + if(pFile->read_size == 0) + { + int size; + size = sceIoRead(pFile->fd, pFile->read_buf, MAX_BUFFER); + if(size > 0) + { + pFile->read_size = size; + pFile->read_pos = 0; + } + else + { + pFile->read_size = 0; + pFile->read_pos = 0; + } + } + + if(pFile->read_pos < pFile->read_size) + { + ch = pFile->read_buf[pFile->read_pos++]; + } + + return ch; +} + +/* As the kernel's fdgetc is broke so is fdgets */ +int fdgets(PspFile *pFile, char *buf, int max) +{ + int pos = 0; + + while(pos < (max-1)) + { + int ch; + + ch = fdgetc(pFile); + + /* EOF */ + if(ch == -1) + { + break; + } + + buf[pos++] = (char) ch; + + if(ch == '\n') + { + break; + } + } + + buf[pos] = 0; + + return pos; +} + +void strip_whitesp(char *s) +{ + int start; + int end; + + end = strlen(s); + while(end > 0) + { + if(is_aspace(s[end-1])) + { + end--; + s[end] = 0; + } + else + { + break; + } + } + + start = 0; + while(s[start]) + { + if(is_aspace(s[start])) + { + start++; + } + else + { + break; + } + } + + if(start > 0) + { + int pos = 0; + while(s[start]) + { + s[pos++] = s[start++]; + } + s[pos] = 0; + } +} + +int strtoint(const char *str, unsigned int *i) +{ + char *endp; + unsigned int val; + + val = strtoul(str, &endp, 0); + if(*endp != 0) + { + return 0; + } + *i = val; + + return 1; +} + +int memcmp_mask(void *data1, void *data2, void *mask, int len) +{ + unsigned char *m, *d1, *d2; + int i; + + m = mask; + d1 = data1; + d2 = data2; + + if(m == NULL) + { + return memcmp(data1, data2, len); + } + + for(i = 0; i < len; i++) + { + if((d1[i] & m[i]) != d2[i]) + { + return (d1[i] & m[i]) - d2[i]; + } + } + + return 0; +} + +void* memmem_mask(void *data, void *mask, int len, void *search, int slen) +{ + int i; + + if((data == NULL) || (len < 0) || (search == NULL) || (slen < 0)) + { + return NULL; + } + + for(i = 0; i < (len - slen + 1); i++) + { + if(memcmp_mask(data, search, mask, slen) == 0) + { + return data; + } + data++; + } + + return NULL; +} + +int decode_hexstr(const char *str, unsigned char *data, int max) +{ + int hexlen; + int i; + + hexlen = strlen(str); + if(hexlen & 1) + { + SHELL_PRINT("Invalid number of hex characters\n"); + return 0; + } + + if((hexlen / 2) > max) + { + SHELL_PRINT("Hex string too long to fit in buffer\n"); + return 0; + } + + for(i = 0; i < (hexlen / 2); i++) + { + if((!is_hex(str[i*2])) || (!is_hex(str[(i*2)+1]))) + { + SHELL_PRINT("Invalid hex byte %.2s\n", &str[i*2]); + return 0; + } + + data[i] = (hex_to_int(str[i*2]) << 4) | hex_to_int(str[(i*2)+1]); + } + + return hexlen / 2; +} + +static SceUID module_refer(SceModule *pMod, SceKernelModuleInfo *info) +{ + SceUID uid = -1; + if(pMod) + { + uid = pMod->modid; + if((info) && (psplinkReferModule(pMod->modid, info) == 0)) + { + uid = -1; + } + } + return uid; +} + +SceUID refer_module_by_addr(unsigned int addr, SceKernelModuleInfo *info) +{ + return module_refer(sceKernelFindModuleByAddress(addr), info); +} + +SceUID refer_module_by_name(const char *name, SceKernelModuleInfo *info) +{ + return module_refer(sceKernelFindModuleByName(name), info); +} + +static unsigned int get_thread_addr(SceUID uid) +{ + SceKernelThreadInfo info; + unsigned int addr = 0; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + if(sceKernelReferThreadStatus(uid, &info) == 0) + { + addr = (unsigned int) info.entry; + } + + return addr; +} + +static unsigned int get_callback_addr(SceUID uid) +{ + SceKernelCallbackInfo info; + unsigned int addr = 0; + + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + if(sceKernelReferCallbackStatus(uid, &info) == 0) + { + addr = (unsigned int) info.callback; + } + + return addr; +} + +int psplinkReferThreadsByModule(int type, SceUID modid, SceUID *uids, int max) +{ + SceUID thids[100]; + SceKernelModuleInfo modinfo; + unsigned int addr; + int count; + int i; + int ret; + + ret = 0; + memset(thids, 0, sizeof(thids)); + if(sceKernelGetThreadmanIdList(type, thids, 100, &count) >= 0) + { + for(i = 0; i < count; i++) + { + switch(type) + { + case SCE_KERNEL_TMID_Thread: addr = get_thread_addr(thids[i]); + break; + case SCE_KERNEL_TMID_Callback: addr = get_callback_addr(thids[i]); + break; + default: addr = 0; + break; + }; + + if(refer_module_by_addr(addr, &modinfo) == modid) + { + if(ret < max) + { + uids[ret++] = thids[i]; + } + } + } + } + + return ret; +} + +SceUID psplinkReferModuleByName(const char *name, SceKernelModuleInfo *info) +{ + int k1; + SceUID uid; + + k1 = psplinkSetK1(0); + uid = refer_module_by_name(name, info); + psplinkSetK1(k1); + + return uid; +} + +int psplinkReferModule(SceUID uid, SceKernelModuleInfo *info) +{ + int ret; + int k1; + + k1 = psplinkSetK1(0); + + memset(info, 0, sizeof(*info)); + info->size = sizeof(*info); + + enable_kprintf(0); + ret = g_QueryModuleInfo(uid, info); + if(ret == 0) + { + ret = 1; + } + else + { + ret = 0; + } + enable_kprintf(1); + + psplinkSetK1(k1); + + return ret; +} + +static int is_nan(float *val) +{ + unsigned int conv; + int sign; + int exp; + int mantissa; + + conv = *((unsigned int *) val); + sign = (conv >> 31) & 1; + + exp = (conv >> 23) & 0xff; + mantissa = conv & 0x7fffff; + + if((exp == 255) && (mantissa != 0)) + { + return 1; + } + + return 0; +} + +static int is_inf(float *val) +{ + unsigned int conv; + int sign; + int exp; + int mantissa; + + conv = *((unsigned int *) val); + sign = (conv >> 31) & 1; + + exp = (conv >> 23) & 0xff; + mantissa = conv & 0x7fffff; + + if((exp == 255) && (mantissa == 0)) + { + if(sign) + { + return -1; + } + else + { + return 1; + } + } + + return 0; +} + +static char get_num(float *val, int *exp) +{ + int digit; + float tmp; + char ret = '0'; + + if((*exp)++ < 16) + { + digit = (int) *val; + if((digit >= 0) && (digit < 10)) + { + ret = digit + '0'; + tmp = (float) digit; + *val = (*val - digit)*10.0f; + } + } + + return ret; +} + +void f_cvt(float *val, char *buf, int bufsize, int precision, int mode) +{ + char conv_buf[128]; + char *conv_p = conv_buf; + float normval; + int digits = 0; + int exp = 0; + int exp_pos = 0; + int inf; + int sign = 0; + float round; + int rndpos = 0; + + /* check for nan and +/- infinity */ + if(is_nan(val)) + { + strncpy(buf, "NaN", bufsize); + buf[bufsize-1] = 0; + return; + } + + inf = is_inf(val); + if(inf != 0) + { + if(inf < 0) + { + strncpy(buf, "-Infinity", bufsize); + buf[bufsize-1] = 0; + } + else + { + strncpy(buf, "Infinity", bufsize); + buf[bufsize-1] = 0; + } + + return; + } + + if(*val < 0.0f) + { + sign = 1; + normval -= *val; + } + else + { + sign = 0; + normval = *val; + } + + if(precision < 0) + { + precision = 6; + } + + /* normalise value */ + if(normval > 0.0f) + { + while((normval >= 1e8f) && (digits++ < 100)) + { + normval *= 1e-8f; + exp += 8; + } + while((normval >= 10.0f) && (digits++ < 100)) + { + normval *= 0.1f; + exp++; + } + while((normval < 1e-8f) && (digits++ < 100)) + { + normval *= 1e8f; + exp -= 8; + } + while((normval < 1.0f) && (digits++ < 100)) + { + normval *= 10.0f; + exp--; + } + } + + for(rndpos = precision, round = 0.4999f; rndpos > 0; rndpos--, round *= 0.1f); + + if(sign) + { + *conv_p++ = '-'; + } + + if((mode != MODE_EXP) && (mode != MODE_FLOAT_ONLY)) + { + if((exp < -4) || (exp > precision)) + { + mode = MODE_EXP; + } + } + + normval += round; + + if(mode == MODE_EXP) + { + *conv_p++ = get_num(&normval, &exp_pos); + *conv_p++ = '.'; + + while(precision > 0) + { + *conv_p++ = get_num(&normval, &exp_pos); + precision--; + } + + while((conv_p[-1] == '0') || (conv_p[-1] == '.')) + { + conv_p--; + } + + *conv_p++ = 'e'; + if(exp < 0) + { + exp = -exp; + *conv_p++ = '-'; + } + else + { + *conv_p++ = '+'; + } + + if(exp / 100) + { + *conv_p++ = (exp / 100) + '0'; + exp %= 100; + } + + if(exp / 10) + { + *conv_p++ = (exp / 10) + '0'; + exp %= 10; + } + *conv_p++ = exp + '0'; + } + else + { + if(exp >= 0) + { + for(; (exp >= 0); exp--) + { + *conv_p++ = get_num(&normval, &exp_pos); + } + } + else + { + *conv_p++ = '0'; + } + + exp++; + if(precision > 0) + { + *conv_p++ = '.'; + } + + for( ; (exp < 0) && (precision > 0); exp++, precision--) + { + *conv_p++ = '0'; + } + + while(precision > 0) + { + *conv_p++ = get_num(&normval, &exp_pos); + precision--; + } + } + + *conv_p = 0; + + strncpy(buf, conv_buf, bufsize); + buf[bufsize-1] = 0; + + return; +} + +unsigned int *get_debug_register(void) +{ + unsigned int *pData; + unsigned int ptr; + + pData = (unsigned int *) (0x80000000 | ((sceKernelRemoveByDebugSection & 0x03FFFFFF) << 2)); + ptr = ((pData[0] & 0xFFFF) << 16) + (short) (pData[2] & 0xFFFF); + + return (unsigned int *) ptr; +} + +int isdir(const char *path) +{ + SceIoStat stat; + char localpath[1024]; + int len; + + strcpy(localpath, path); + len = strlen(localpath); + if(len < 2) /* Not a valid full path */ + { + return 0; + } + + if(localpath[len-1] == '/') + { + /* Absolute directory, fix for dodgy Sony drivers */ + if((localpath[len-2] == ':') && (strchr(localpath, '/') == &localpath[len-1])) + { + return 1; + } + + localpath[len-1] = 0; + } + + if(sceIoGetstat(localpath, &stat) == 0) + { + if(FIO_SO_ISDIR(stat.st_attr) || FIO_S_ISDIR(stat.st_mode)) + { + return 1; + } + } + + return 0; +} + +void enable_kprintf(int enable) +{ + unsigned int *pData; + + pData = get_debug_register(); + if(enable) + { + *pData |= DEBUG_REG_KPRINTF_ENABLE; + } + else + { + *pData &= ~DEBUG_REG_KPRINTF_ENABLE; + } +} diff --git a/psplink/util.h b/psplink/util.h new file mode 100644 index 0000000..9c00141 --- /dev/null +++ b/psplink/util.h @@ -0,0 +1,82 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * util.h - header file for util.c + * + * Copyright (c) 2005 Julian T + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/util.h $ + * $Id: util.h 2316 2007-09-17 18:13:33Z tyranid $ + */ + +#ifndef __UTIL_H__ +#define __UTIL_H__ + +#define MAX_BUFFER 4096 +#define TYPE_FILE 1 +#define TYPE_DIR 2 +#define TYPE_ETHER 3 + +typedef struct _PspFile +{ + int fd; + char read_buf[MAX_BUFFER]; + int read_size; + int read_pos; +} PspFile; + +int is_hex(char ch); +int is_oct(char ch); +int hex_to_int(char ch); +int oct_to_int(char ch); +char upcase(char ch); +int is_aspace(int ch); +int is_alnum(char ch); +int build_bootargs(char *args, const char *bootfile, const char *execfile, int argc, char **argv); +int build_args(char *args, const char *execfile, int argc, char **argv); +int handlepath(const char *currentdir, const char *relative, char *path, int type, int validate); +int findinpath(const char *relative, char *output, const char *pathvar); +int load_start_module(const char *name, int argc, char **argv); +int load_start_module_debug(const char *name); +void map_firmwarerev(void); +int init_usbmass(void); +int stop_usbmass(void); +int init_usbhost(const char *bootpath); +int stop_usbhost(void); +void save_execargs(int argc, char **argv); +int openfile(const char *filename, PspFile *pFile); +int closefile(PspFile *pFile); +int fdgetc(PspFile *pFile); +int fdgets(PspFile *pFile, char *buf, int size); +void strip_whitesp(char *s); +int strtoint(const char *str, unsigned int *i); +void* memmem_mask(void *data, void *mask, int len, void *search, int slen); +int memcmp_mask(void *data1, void *data2, void *mask, int len); +int decode_hexstr(const char *str, unsigned char *data, int max); +SceUID refer_module_by_addr(unsigned int addr, SceKernelModuleInfo *info); +SceUID refer_module_by_name(const char *name, SceKernelModuleInfo *info); +int psplinkReferThreadsByModule(int type, SceUID modid, SceUID *uids, int max); +int psplinkReferModule(SceUID uid, SceKernelModuleInfo *info); +SceUID psplinkReferModuleByName(const char *name, SceKernelModuleInfo *info); +int isdir(const char *path); + +#define DEBUG_REG_KPRINTF_ENABLE 0x00001000 +#define DEBUG_REG_GLOBAL_PROFILER 0x00800000 +#define DEBUG_REG_THREAD_PROFILER 0x00C00000 +#define DEBUG_REG_PROFILER_MASK (~DEBUG_REG_THREAD_PROFILER) +unsigned int *get_debug_register(void); +void enable_kprintf(int enable); + +#define MODE_GENERIC 0 +#define MODE_EXP 1 +#define MODE_FLOAT_ONLY 2 +void f_cvt(float *val, char *buf, int bufsize, int precision, int mode); + +extern int (*g_QueryModuleInfo)(SceUID modid, SceKernelModuleInfo *info); +extern int (*g_GetModuleIdList)(SceUID *readbuf, int readbufsize, int *idcount); +extern int g_isv1; + +#endif diff --git a/psplink/version.h b/psplink/version.h new file mode 100644 index 0000000..3618c9c --- /dev/null +++ b/psplink/version.h @@ -0,0 +1,19 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * version.h - PSPLink version string + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink/version.h $ + * $Id: version.h 2223 2007-04-23 19:13:10Z tyranid $ + */ + +#ifndef __VERSION_H__ +#define __VERSION_H__ + +#define PSPLINK_VERSION "v3.0" + +#endif diff --git a/psplink_manual.pdf b/psplink_manual.pdf new file mode 100644 index 0000000..f489ba6 Binary files /dev/null and b/psplink_manual.pdf differ diff --git a/psplink_manual.sxw b/psplink_manual.sxw new file mode 100644 index 0000000..063ebfe Binary files /dev/null and b/psplink_manual.sxw differ diff --git a/psplink_user/Makefile b/psplink_user/Makefile new file mode 100644 index 0000000..ca47c4c --- /dev/null +++ b/psplink_user/Makefile @@ -0,0 +1,18 @@ +TARGET = psplink_user +OBJS = main.o kmode.o + +BUILD_PRX=1 +PRX_EXPORTS=exports.exp + +INCDIR = +CFLAGS = -O2 -G0 -Wall -I../libpsplink +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +LDFLAGS= -mno-crt0 -nostartfiles -L../libpsplink +ASFLAGS = $(CFLAGS) + +LIBDIR = + +LIBS = -lpsplink + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build.mak diff --git a/psplink_user/exports.exp b/psplink_user/exports.exp new file mode 100644 index 0000000..b7940e8 --- /dev/null +++ b/psplink_user/exports.exp @@ -0,0 +1,12 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/psplink_user/kmode.S b/psplink_user/kmode.S new file mode 100644 index 0000000..da720e7 --- /dev/null +++ b/psplink_user/kmode.S @@ -0,0 +1,161 @@ + + .set noreorder + .set noat + + .global _apiHookHandle + .global _apiHookReturn + + .global _apiHookEntry + .ent _apiHookEntry +_apiHookEntry: + addiu $sp, $sp, -(4*8) # Allocate space for 6 args + $ra + hooknum + sw $ra, 0($sp) # Save $ra and args + sw $a0, 4($sp) + sw $a1, 8($sp) + sw $a2, 12($sp) + sw $a3, 16($sp) + sw $t0, 20($sp) + sw $t1, 24($sp) + sw $v0, 28($sp) + move $a0, $v0 # Copy api hook number into first arg + jal _apiHookHandle + addiu $a1, $sp, 4 # Copy pointer to args into second arg + + lw $a0, 4($sp) # Restore args + lw $a1, 8($sp) + lw $a2, 12($sp) + lw $a3, 16($sp) + + beq $v0, $0, 1f + lw $t0, 20($sp) + + jalr $v0 # Will call with a 32 byte overhead + lw $t1, 24($sp) + + sw $v0, 4($sp) # Save return values + sw $v1, 8($sp) + lw $a0, 28($sp) # Load hook address + + jal _apiHookReturn # Print return values + addiu $a1, $sp, 4 # Copy pointer to return values into second arg + + lw $v0, 4($sp) # Restore return values + lw $v1, 4($sp) + +1: + lw $ra, 0($sp) # Restore $ra + jr $ra + addiu $sp, $sp, (4*8) + .end _apiHookEntry + + .global _apiHook0 + .ent _apiHook0 +_apiHook0: + j _apiHookEntry + li $v0, 0 + .end _apiHook0 + + .global _apiHook1 + .ent _apiHook1 +_apiHook1: + j _apiHookEntry + li $v0, 1 + .end _apiHook1 + + .global _apiHook2 + .ent _apiHook2 +_apiHook2: + j _apiHookEntry + li $v0, 2 + .end _apiHook2 + + .global _apiHook3 + .ent _apiHook3 +_apiHook3: + j _apiHookEntry + li $v0, 3 + .end _apiHook3 + + .global _apiHook4 + .ent _apiHook4 +_apiHook4: + j _apiHookEntry + li $v0, 4 + .end _apiHook4 + + .global _apiHook5 + .ent _apiHook5 +_apiHook5: + j _apiHookEntry + li $v0, 5 + .end _apiHook5 + + .global _apiHook6 + .ent _apiHook6 +_apiHook6: + j _apiHookEntry + li $v0, 6 + .end _apiHook6 + + .global _apiHook7 + .ent _apiHook7 +_apiHook7: + j _apiHookEntry + li $v0, 7 + .end _apiHook7 + + .global _apiHook8 + .ent _apiHook8 +_apiHook8: + j _apiHookEntry + li $v0, 8 + .end _apiHook8 + + .global _apiHook9 + .ent _apiHook9 +_apiHook9: + j _apiHookEntry + li $v0, 9 + .end _apiHook9 + + .global _apiHook10 + .ent _apiHook10 +_apiHook10: + j _apiHookEntry + li $v0, 10 + .end _apiHook10 + + .global _apiHook11 + .ent _apiHook11 +_apiHook11: + j _apiHookEntry + li $v0, 11 + .end _apiHook11 + + .global _apiHook12 + .ent _apiHook12 +_apiHook12: + j _apiHookEntry + li $v0, 12 + .end _apiHook12 + + .global _apiHook13 + .ent _apiHook13 +_apiHook13: + j _apiHookEntry + li $v0, 13 + .end _apiHook13 + + .global _apiHook14 + .ent _apiHook14 +_apiHook14: + j _apiHookEntry + li $v0, 14 + .end _apiHook14 + + .global _apiHook15 + .ent _apiHook15 +_apiHook15: + j _apiHookEntry + li $v0, 15 + .end _apiHook15 diff --git a/psplink_user/main.c b/psplink_user/main.c new file mode 100644 index 0000000..574d515 --- /dev/null +++ b/psplink_user/main.c @@ -0,0 +1,38 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - Main code for PSPLINK user module. + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink_user/main.c $ + * $Id: main.c 2173 2007-02-07 18:51:48Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include "psplink_user.h" + +void _apiHook0(void); +void apiHookRegisterUserDispatch(unsigned int *); + +PSP_MODULE_INFO("PSPLINK_USER", 0, 1, 1); +PSP_MAIN_THREAD_PARAMS(0x20, 64, PSP_THREAD_ATTR_USER); +PSP_MAIN_THREAD_NAME("PsplinkUser"); + +int module_start(int args, void *argp) +{ + apiHookRegisterUserDispatch((unsigned int *) _apiHook0); + + return 0; +} + +int module_stop(int args, void *argp) +{ + return 0; +} diff --git a/psplink_user/psplink_user.h b/psplink_user/psplink_user.h new file mode 100644 index 0000000..42a3f54 --- /dev/null +++ b/psplink_user/psplink_user.h @@ -0,0 +1,16 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * psplink_user.h -Header for PSPLINK user module. + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/psplink_user/psplink_user.h $ + * $Id: psplink_user.h 2170 2007-02-06 00:07:32Z tyranid $ + */ +#ifndef __PSPLINK_USER_H__ +#define __PSPLINK_USER_H__ + +#endif diff --git a/pspsh/Makefile b/pspsh/Makefile new file mode 100644 index 0000000..3d77eca --- /dev/null +++ b/pspsh/Makefile @@ -0,0 +1,20 @@ +OUTPUT=pspsh +OBJS=pspsh.o parse_args.o pspkerror.o asm.o disasm.o + +CXXFLAGS=-Wall -g -D_PCTERM -I../psplink +LIBS=-lreadline -lcurses + +PREFIX=$(shell psp-config --pspdev-path 2> /dev/null) + +all: $(OUTPUT) + +$(OUTPUT): $(OBJS) + $(CXX) -o $@ $^ $(LIBS) + +install: $(OUTPUT) + @echo "Installing $(OUTPUT)..." + @if ( test $(PREFIX) ); then { mkdir -p $(PREFIX)/bin && cp $(OUTPUT) $(PREFIX)/bin; } else { echo "Error: psp-config not found!"; exit 1; } fi + @echo "Done!" + +clean: + rm -f $(OUTPUT) *.o diff --git a/pspsh/asm.C b/pspsh/asm.C new file mode 100644 index 0000000..2371d00 --- /dev/null +++ b/pspsh/asm.C @@ -0,0 +1,1225 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * asm.c - PSPLINK pc terminal simple MIPS assembler + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/pspsh/asm.C $ + * $Id: asm.C 2200 2007-03-08 21:21:20Z tyranid $ + */ +#include +#include +#include +#include +#include + +/* Format codes + * %d - Rd + * %t - Rt + * %s - Rs + * %i - 16bit signed immediate + * %I - 16bit unsigned immediate (always printed in hex) + * %o - 16bit signed offset (rt base) + * %O - 16bit signed offset (PC relative) + * %V - 16bit signed offset (rs base) + * %j - 26bit absolute offset + * %a - SA + * %0 - Cop0 register + * %1 - Cop1 register + * %p - General cop (i.e. numbered) register + * %n - ins/ext size + * %r - Debug register + * %k - Cache function + * %D - Fd + * %T - Ft + * %S - Fs + * %x? - Vt (? is (s/scalar, p/pair, t/triple, q/quad, m/matrix pair, n/matrix triple, o/matrix quad) + * %y? - Vs + * %z? - Vd + * %X? - Vo (? is (s, q)) + * %Y - VFPU offset + * %Z - VFPU condition code + * %v? - VFPU immediate, ? (3, 5, 8) + * %c - code (for break) + * %C - code (for syscall) + * %? - Indicates vmmul special exception + */ + +#define RT(op) ((op >> 16) & 0x1F) +#define RS(op) ((op >> 21) & 0x1F) +#define RD(op) ((op >> 11) & 0x1F) +#define FT(op) ((op >> 16) & 0x1F) +#define FS(op) ((op >> 11) & 0x1F) +#define FD(op) ((op >> 6) & 0x1F) +#define SA(op) ((op >> 6) & 0x1F) +#define IMM(op) ((signed short) (op & 0xFFFF)) +#define IMMU(op) ((unsigned short) (op & 0xFFFF)) +#define JUMP(op, pc) ((pc & 0xF0000000) | ((op & 0x3FFFFFF) << 2)) +#define CODE(op) ((op >> 6) & 0xFFFFF) +#define SIZE(op) ((op >> 11) & 0x1F) +#define POS(op) ((op >> 6) & 0x1F) +#define VO(op) (((op & 3) << 5) | ((op >> 16) & 0x1F)) +#define VCC(op) ((op >> 18) & 7) +#define VD(op) (op & 0x7F) +#define VS(op) ((op >> 8) & 0x7F) +#define VT(op) ((op >> 16) & 0x7F) + +#define SETRT(op, reg) (op |= ((reg) << 16)) +#define SETRS(op, reg) (op |= ((reg) << 21)) +#define SETRD(op, reg) (op |= ((reg) << 11)) +#define SETFT(op, reg) (op |= ((reg) << 16)) +#define SETFS(op, reg) (op |= ((reg) << 11)) +#define SETFD(op, reg) (op |= ((reg) << 6)) +#define SETSA(op, val) (op |= ((val) << 6)) + +#if 0 +#define SETIMM(op, imm) ((signed short) (op & 0xFFFF)) +#define SETIMMU(op, imm) ((unsigned short) (op & 0xFFFF)) +#define SETJUMP(op, addr) ((0xF0000000) | ((op & 0x3FFFFFF) << 2)) +#define SETCODE(op, code) ((op >> 6) & 0xFFFFF) +#define SETSIZE(op, size) ((op >> 11) & 0x1F) +#define SETPOS(op, pos) ((op >> 6) & 0x1F) +#define SETVO(op, vo) (((op & 3) << 5) | ((op >> 16) & 0x1F)) +#define SETVCC(op, vcc) ((op >> 18) & 7) +#define SETVD(op, vd) (op & 0x7F) +#define SETVS(op, vs) ((op >> 8) & 0x7F) +#define SETVT(op, vt) ((op >> 16) & 0x7F) +#endif + +struct Instruction +{ + const char *name; + unsigned int opcode; + unsigned int mask; + const char *fmt; + int operands; +}; + +#define INSTR_TYPE_PSP 1 +#define INSTR_TYPE_B 2 +#define INSTR_TYPE_JUMP 4 +#define INSTR_TYPE_JAL 8 + +#define INSTR_TYPE_BRANCH (INSTR_TYPE_B | INSTR_TYPE_JUMP | INSTR_TYPE_JAL) + +#define ADDR_TYPE_NONE 0 +#define ADDR_TYPE_16 1 +#define ADDR_TYPE_26 2 +#define ADDR_TYPE_REG 3 + +const char *regName[32] = +{ + "zr", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" +}; + +struct Instruction macro[] = +{ + /* Macro instructions */ + { "nop", 0x00000000, 0xFFFFFFFF, "" , 0 }, + { "li", 0x24000000, 0xFFE00000, "%t, %i" , 2 }, + { "li", 0x34000000, 0xFFE00000, "%t, %I" , 2 }, + { "move", 0x00000021, 0xFC1F07FF, "%d, %s" , 2 }, + { "move", 0x00000025, 0xFC1F07FF, "%d, %s" , 2 }, + { "b", 0x10000000, 0xFFFF0000, "%O" , 1 }, + { "b", 0x04010000, 0xFFFF0000, "%O" , 1 }, + { "bal", 0x04110000, 0xFFFF0000, "%O" , 1 }, + { "bnez", 0x14000000, 0xFC1F0000, "%s, %O" , 2 }, + { "bnezl", 0x54000000, 0xFC1F0000, "%s, %O" , 2 }, + { "beqz", 0x10000000, 0xFC1F0000, "%s, %O", 2 }, + { "beqzl", 0x50000000, 0xFC1F0000, "%s, %O", 2 }, + { "neg", 0x00000022, 0xFFE007FF, "%d, %t" , 2 }, + { "negu", 0x00000023, 0xFFE007FF, "%d, %t" , 2 }, + { "not", 0x00000027, 0xFC1F07FF, "%d, %s" , 2 }, + { "jalr", 0x0000F809, 0xFC1FFFFF, "%s", 1 }, +}; + +static struct Instruction g_inst[] = +{ + /* MIPS instructions */ + { "add", 0x00000020, 0xFC0007FF, "%d, %s, %t", 3 }, + { "addi", 0x20000000, 0xFC000000, "%t, %s, %i", 3 }, + { "addiu", 0x24000000, 0xFC000000, "%t, %s, %i", 3 }, + { "addu", 0x00000021, 0xFC0007FF, "%d, %s, %t", 3 }, + { "and", 0x00000024, 0xFC0007FF, "%d, %s, %t", 3 }, + { "andi", 0x30000000, 0xFC000000, "%t, %s, %I", 3 }, + { "beq", 0x10000000, 0xFC000000, "%s, %t, %O", 3 }, + { "beql", 0x50000000, 0xFC000000, "%s, %t, %O", 3 }, + { "bgez", 0x04010000, 0xFC1F0000, "%s, %O", 2 }, + { "bgezal", 0x04110000, 0xFC1F0000, "%s, %0", 2 }, + { "bgezl", 0x04030000, 0xFC1F0000, "%s, %O", 2 }, + { "bgtz", 0x1C000000, 0xFC1F0000, "%s, %O", 2 }, + { "bgtzl", 0x5C000000, 0xFC1F0000, "%s, %O", 2 }, + { "bitrev", 0x7C000520, 0xFFE007FF, "%d, %t", 2 }, + { "blez", 0x18000000, 0xFC1F0000, "%s, %O", 2 }, + { "blezl", 0x58000000, 0xFC1F0000, "%s, %O", 2 }, + { "bltz", 0x04000000, 0xFC1F0000, "%s, %O", 2 }, + { "bltzl", 0x04020000, 0xFC1F0000, "%s, %O", 2 }, + { "bltzal", 0x04100000, 0xFC1F0000, "%s, %O", 2 }, + { "bltzall", 0x04120000, 0xFC1F0000, "%s, %O", 2 }, + { "bne", 0x14000000, 0xFC000000, "%s, %t, %O", 3 }, + { "bnel", 0x54000000, 0xFC000000, "%s, %t, %O", 3 }, + { "break", 0x0000000D, 0xFC00003F, "%c", 1 }, + { "cache", 0xBC000000, 0xFC000000, "%k, %o", 2 }, + { "cfc0", 0x40400000, 0xFFE007FF, "%t, %p", 2 }, + { "clo", 0x00000017, 0xFC1F07FF, "%d, %s", 2 }, + { "clz", 0x00000016, 0xFC1F07FF, "%d, %s", 2 }, + { "ctc0", 0x40C00000, 0xFFE007FF, "%t, %p", 2 }, + { "max", 0x0000002C, 0xFC0007FF, "%d, %s, %t", 3 }, + { "min", 0x0000002D, 0xFC0007FF, "%d, %s, %t", 3 }, + { "dbreak", 0x7000003F, 0xFFFFFFFF, "", 0 }, + { "div", 0x0000001A, 0xFC00FFFF, "%s, %t", 2 }, + { "divu", 0x0000001B, 0xFC00FFFF, "%s, %t", 2 }, + { "dret", 0x7000003E, 0xFFFFFFFF, "", 0 }, + { "eret", 0x42000018, 0xFFFFFFFF, "", 0 }, + { "ext", 0x7C000000, 0xFC00003F, "%t, %s, %a, %n", 4 }, + { "ins", 0x7C000004, 0xFC00003F, "%t, %s, %a, %n", 4 }, + { "j", 0x08000000, 0xFC000000, "%j", 1 }, + { "jr", 0x00000008, 0xFC1FFFFF, "%s", 1 }, + { "jalr", 0x00000009, 0xFC1F07FF, "%s, %d", 2 }, + { "jal", 0x0C000000, 0xFC000000, "%j", 1 }, + { "lb", 0x80000000, 0xFC000000, "%t, %o", 2 }, + { "lbu", 0x90000000, 0xFC000000, "%t, %o", 2 }, + { "lh", 0x84000000, 0xFC000000, "%t, %o", 2 }, + { "lhu", 0x94000000, 0xFC000000, "%t, %o", 2 }, + { "ll", 0xC0000000, 0xFC000000, "%t, %O", 2 }, + { "lui", 0x3C000000, 0xFFE00000, "%t, %I", 2 }, + { "lw", 0x8C000000, 0xFC000000, "%t, %o", 2 }, + { "lwl", 0x88000000, 0xFC000000, "%t, %o", 2 }, + { "lwr", 0x98000000, 0xFC000000, "%t, %o", 2 }, + { "madd", 0x0000001C, 0xFC00FFFF, "%s, %t", 2 }, + { "maddu", 0x0000001D, 0xFC00FFFF, "%s, %t", 2 }, + { "mfc0", 0x40000000, 0xFFE007FF, "%t, %0", 2 }, + { "mfdr", 0x7000003D, 0xFFE007FF, "%t, %r", 2 }, + { "mfhi", 0x00000010, 0xFFFF07FF, "%d", 1 }, + { "mfic", 0x70000024, 0xFFE007FF, "%t, %p", 2 }, + { "mflo", 0x00000012, 0xFFFF07FF, "%d", 1 }, + { "movn", 0x0000000B, 0xFC0007FF, "%d, %s, %t", 3 }, + { "movz", 0x0000000A, 0xFC0007FF, "%d, %s, %t", 3 }, + { "msub", 0x0000002e, 0xfc00ffff, "%d, %t", 2 }, + { "msubu", 0x0000002f, 0xfc00ffff, "%d, %t", 2 }, + { "mtc0", 0x40800000, 0xFFE007FF, "%t, %0", 2 }, + { "mtdr", 0x7080003D, 0xFFE007FF, "%t, %r", 2 }, + { "mtic", 0x70000026, 0xFFE007FF, "%t, %p", 2 }, + { "halt", 0x70000000, 0xFFFFFFFF, "" , 0 }, + { "mthi", 0x00000011, 0xFC1FFFFF, "%s", 1 }, + { "mtlo", 0x00000013, 0xFC1FFFFF, "%s", 1 }, + { "mult", 0x00000018, 0xFC00FFFF, "%s, %t", 2 }, + { "multu", 0x00000019, 0xFC0007FF, "%s, %t", 2 }, + { "nor", 0x00000027, 0xFC0007FF, "%d, %s, %t", 3 }, + { "or", 0x00000025, 0xFC0007FF, "%d, %s, %t", 3 }, + { "ori", 0x34000000, 0xFC000000, "%t, %s, %I", 3 }, + { "rotr", 0x00200002, 0xFFE0003F, "%d, %t, %a", 3 }, + { "rotv", 0x00000046, 0xFC0007FF, "%d, %t, %s", 3 }, + { "seb", 0x7C000420, 0xFFE007FF, "%d, %t", 2 }, + { "seh", 0x7C000620, 0xFFE007FF, "%d, %t", 2 }, + { "sb", 0xA0000000, 0xFC000000, "%t, %o", 2 }, + { "sh", 0xA4000000, 0xFC000000, "%t, %o", 2 }, + { "sllv", 0x00000004, 0xFC0007FF, "%d, %t, %s", 3 }, + { "sll", 0x00000000, 0xFFE0003F, "%d, %t, %a", 3 }, + { "slt", 0x0000002A, 0xFC0007FF, "%d, %s, %t", 3 }, + { "slti", 0x28000000, 0xFC000000, "%t, %s, %i", 3 }, + { "sltiu", 0x2C000000, 0xFC000000, "%t, %s, %i", 3 }, + { "sltu", 0x0000002B, 0xFC0007FF, "%d, %s, %t", 3 }, + { "sra", 0x00000003, 0xFFE0003F, "%d, %t, %a", 3 }, + { "srav", 0x00000007, 0xFC0007FF, "%d, %t, %s", 3 }, + { "srlv", 0x00000006, 0xFC0007FF, "%d, %t, %s", 3 }, + { "srl", 0x00000002, 0xFFE0003F, "%d, %t, %a", 3 }, + { "sw", 0xAC000000, 0xFC000000, "%t, %o", 2 }, + { "swl", 0xA8000000, 0xFC000000, "%t, %o", 2 }, + { "swr", 0xB8000000, 0xFC000000, "%t, %o", 2 }, + { "sub", 0x00000022, 0xFC0007FF, "%d, %s, %t", 3 }, + { "subu", 0x00000023, 0xFC0007FF, "%d, %s, %t", 3 }, + { "sync", 0x0000000F, 0xFFFFFFFF, "", 0 }, + { "syscall", 0x0000000C, 0xFC00003F, "%C", 1 }, + { "xor", 0x00000026, 0xFC0007FF, "%d, %s, %t", 3 }, + { "xori", 0x38000000, 0xFC000000, "%t, %s, %I", 3 }, + { "wsbh", 0x7C0000A0, 0xFFE007FF, "%d, %t", 2 }, + { "wsbw", 0x7C0000E0, 0xFFE007FF, "%d, %t", 2 }, + + /* We dont currently support fpu or vfpu */ +#if 0 + /* FPU instructions */ + {"abs.s", 0x46000005, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"add.s", 0x46000000, 0xFFE0003F, "%D, %S, %T", ADDR_TYPE_NONE, 0 }, + {"bc1f", 0x45000000, 0xFFFF0000, "%O", ADDR_TYPE_16, INSTR_TYPE_B }, + {"bc1fl", 0x45020000, 0xFFFF0000, "%O", ADDR_TYPE_16, INSTR_TYPE_B }, + {"bc1t", 0x45010000, 0xFFFF0000, "%O", ADDR_TYPE_16, INSTR_TYPE_B }, + {"bc1tl", 0x45030000, 0xFFFF0000, "%O", ADDR_TYPE_16, INSTR_TYPE_B }, + {"c.f.s", 0x46000030, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.un.s", 0x46000031, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.eq.s", 0x46000032, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ueq.s", 0x46000033, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.olt.s", 0x46000034, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ult.s", 0x46000035, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ole.s", 0x46000036, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ule.s", 0x46000037, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.sf.s", 0x46000038, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ngle.s",0x46000039, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.seq.s", 0x4600003A, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ngl.s", 0x4600003B, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.lt.s", 0x4600003C, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.nge.s", 0x4600003D, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.le.s", 0x4600003E, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ngt.s", 0x4600003F, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"ceil.w.s",0x4600000E, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"cfc1", 0x44400000, 0xFFE007FF, "%t, %p", ADDR_TYPE_NONE, 0 }, + {"ctc1", 0x44c00000, 0xFFE007FF, "%t, %p", ADDR_TYPE_NONE, 0 }, + {"cvt.s.w", 0x46800020, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"cvt.w.s", 0x46000024, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"div.s", 0x46000003, 0xFFE0003F, "%D, %S, %T", ADDR_TYPE_NONE, 0 }, + {"floor.w.s",0x4600000F, 0xFFFF003F,"%D, %S", ADDR_TYPE_NONE, 0 }, + {"lwc1", 0xc4000000, 0xFC000000, "%T, %o", ADDR_TYPE_NONE, 0 }, + {"mfc1", 0x44000000, 0xFFE007FF, "%t, %1", ADDR_TYPE_NONE, 0 }, + {"mov.s", 0x46000006, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"mtc1", 0x44800000, 0xFFE007FF, "%t, %1", ADDR_TYPE_NONE, 0 }, + {"mul.s", 0x46000002, 0xFFE0003F, "%D, %S, %T", ADDR_TYPE_NONE, 0 }, + {"neg.s", 0x46000007, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"round.w.s",0x4600000C, 0xFFFF003F,"%D, %S", ADDR_TYPE_NONE, 0 }, + {"sqrt.s", 0x46000004, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"sub.s", 0x46000001, 0xFFE0003F, "%D, %S, %T", ADDR_TYPE_NONE, 0 }, + {"swc1", 0xe4000000, 0xFC000000, "%T, %o", ADDR_TYPE_NONE, 0 }, + {"trunc.w.s",0x4600000D, 0xFFFF003F,"%D, %S", ADDR_TYPE_NONE, 0 }, + + /* VPU instructions */ + { "bvf", 0x49000000, 0xFFE30000, "%Z, %O" , ADDR_TYPE_16, INSTR_TYPE_PSP | INSTR_TYPE_B }, + { "bvfl", 0x49020000, 0xFFE30000, "%Z, %O" , ADDR_TYPE_16, INSTR_TYPE_PSP | INSTR_TYPE_B }, + { "bvt", 0x49010000, 0xFFE30000, "%Z, %O" , ADDR_TYPE_16, INSTR_TYPE_PSP | INSTR_TYPE_B }, + { "bvtl", 0x49030000, 0xFFE30000, "%Z, %O" , ADDR_TYPE_16, INSTR_TYPE_PSP | INSTR_TYPE_B }, + { "lv.q", 0xD8000000, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "lv.s", 0xC8000000, 0xFC000000, "%Xs, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "lvl.q", 0xD4000000, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "lvr.q", 0xD4000002, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mfv", 0x48600000, 0xFFE0FF80, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mfvc", 0x48600000, 0xFFE0FF00, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mtv", 0x48E00000, 0xFFE0FF80, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mtvc", 0x48E00000, 0xFFE0FF00, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "sv.q", 0xF8000000, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "sv.s", 0xE8000000, 0xFC000000, "%Xs, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "svl.q", 0xF4000000, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "svr.q", 0xF4000002, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vabs.p", 0xD0010080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vabs.q", 0xD0018080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vabs.s", 0xD0010000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vabs.t", 0xD0018000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vadd.p", 0x60000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vadd.q", 0x60008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vadd.s", 0x60000000, 0xFF808080, "%zs, %yz, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vadd.t", 0x60008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vasin.p", 0xD0170080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vasin.q", 0xD0178080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vasin.s", 0xD0170000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vasin.t", 0xD0178000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vavg.p", 0xD0470080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vavg.q", 0xD0478080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vavg.t", 0xD0478000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vbfy1.p", 0xD0420080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vbfy1.q", 0xD0428080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vbfy2.q", 0xD0438080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmovf.p", 0xD2A80080, 0xFFF88080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmovf.q",0xD2A88080, 0xFFF88080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmovf.s", 0xD2A80000, 0xFFF88080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmovf.t",0xD2A88000, 0xFFF88080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmovt.p", 0xD2A00080, 0xFFF88080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmovt.q",0xD2A08080, 0xFFF88080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmovt.s", 0xD2A00000, 0xFFF88080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmovt.t",0xD2A08000, 0xFFF88080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.p", 0x6C000080, 0xFF8080F0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.p", 0x6C000080, 0xFFFF80F0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.p", 0x6C000080, 0xFFFFFFF0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.q", 0x6C008080, 0xFF8080F0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.q", 0x6C008080, 0xFFFF80F0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.q", 0x6C008080, 0xFFFFFFF0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.s", 0x6C000000, 0xFF8080F0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.s", 0x6C000000, 0xFFFF80F0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.s", 0x6C000000, 0xFFFFFFF0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.t", 0x6C008000, 0xFF8080F0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.t", 0x6C008000, 0xFFFF80F0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmp.t", 0x6C008000, 0xFFFFFFF0, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcos.p", 0xD0130080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcos.q", 0xD0138080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcos.s", 0xD0130000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcos.t", 0xD0138000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcrs.t", 0x66808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcrsp.t", 0xF2808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcst.p", 0xD0600080, 0xFFE0FF80, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcst.q", 0xD0608080, 0xFFE0FF80, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcst.s", 0xD0600000, 0xFFE0FF80, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcst.t", 0xD0608000, 0xFFE0FF80, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdet.p", 0x67000080, 0xFF808080, "%zs, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdiv.p", 0x63800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdiv.q", 0x63808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdiv.s", 0x63800000, 0xFF808080, "%zs, %yz, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdiv.t", 0x63808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdot.p", 0x64800080, 0xFF808080, "%zs, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdot.q", 0x64808080, 0xFF808080, "%zs, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdot.t", 0x64808000, 0xFF808080, "%zs, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vexp2.p", 0xD0140080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vexp2.q", 0xD0148080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vexp2.s", 0xD0140000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vexp2.t", 0xD0148000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2h.p", 0xD0320080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2h.q", 0xD0328080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2id.p", 0xD2600080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2id.q", 0xD2608080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2id.s", 0xD2600000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2id.t", 0xD2608000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2in.p", 0xD2000080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2in.q", 0xD2008080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2in.s", 0xD2000000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2in.t", 0xD2008000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2iu.p", 0xD2400080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2iu.q", 0xD2408080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2iu.s", 0xD2400000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2iu.t", 0xD2408000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2iz.p", 0xD2200080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2iz.q", 0xD2208080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2iz.s", 0xD2200000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2iz.t", 0xD2208000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vfad.p", 0xD0460080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vfad.q", 0xD0468080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vfad.t", 0xD0468000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vfim.s", 0xDF800000, 0xFF800000, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vflush", 0xFFFF040D, 0xFFFFFFFF, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vh2f.p", 0xD0330080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vh2f.s", 0xD0330000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vhdp.p", 0x66000080, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vhdp.q", 0x66008080, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vhdp.t", 0x66008000, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vhtfm2.p", 0xF0800000, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vhtfm3.t",0xF1000080, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vhtfm4.q",0xF1808000, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2c.q", 0xD03D8080, 0xFFFF8080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2f.p", 0xD2800080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2f.q", 0xD2808080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2f.s", 0xD2800000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2f.t", 0xD2808000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2s.p", 0xD03F0080, 0xFFFF8080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2s.q", 0xD03F8080, 0xFFFF8080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2uc.q", 0xD03C8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2us.p", 0xD03E0080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vi2us.q", 0xD03E8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vidt.p", 0xD0030080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vidt.q", 0xD0038080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "viim.s", 0xDF000000, 0xFF800000, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vlgb.s", 0xD0370000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vlog2.p", 0xD0150080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vlog2.q", 0xD0158080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vlog2.s", 0xD0150000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vlog2.t", 0xD0158000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmax.p", 0x6D800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmax.q", 0x6D808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmax.s", 0x6D800000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmax.t", 0x6D808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmfvc", 0xD0500000, 0xFFFF0080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmidt.p", 0xF3830080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmidt.q", 0xF3838080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmidt.t", 0xF3838000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmin.p", 0x6D000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmin.q", 0x6D008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmin.s", 0x6D000000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmin.t", 0x6D008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmmov.p", 0xF3800080, 0xFFFF8080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmmov.q", 0xF3808080, 0xFFFF8080, "%zo, %yo" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmmov.t", 0xF3808000, 0xFFFF8080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmmul.p", 0xF0000080, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmmul.q", 0xF0008080, 0xFF808080, "%?%zo, %yo, %xo" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmmul.t", 0xF0008000, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmone.p", 0xF3870080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmone.q", 0xF3878080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmone.t", 0xF3878000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmov.p", 0xD0000080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmov.q", 0xD0008080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmov.s", 0xD0000000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmov.t", 0xD0008000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmscl.p", 0xF2000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmscl.q", 0xF2008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmscl.t", 0xF2008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmtvc", 0xD0510000, 0xFFFF8000, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmul.p", 0x64000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmul.q", 0x64008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmul.s", 0x64000000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmul.t", 0x64008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmzero.p", 0xF3860080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmzero.q",0xF3868080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmzero.t",0xF3868000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vneg.p", 0xD0020080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vneg.q", 0xD0028080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vneg.s", 0xD0020000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vneg.t", 0xD0028000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnop", 0xFFFF0000, 0xFFFFFFFF, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnrcp.p", 0xD0180080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnrcp.q", 0xD0188080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnrcp.s", 0xD0180000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnrcp.t", 0xD0188000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnsin.p", 0xD01A0080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnsin.q", 0xD01A8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnsin.s", 0xD01A0000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnsin.t", 0xD01A8000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vocp.p", 0xD0440080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vocp.q", 0xD0448080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vocp.s", 0xD0440000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vocp.t", 0xD0448000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vone.p", 0xD0070080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vone.q", 0xD0078080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vone.s", 0xD0070000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vone.t", 0xD0078000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vpfxd", 0xDE000000, 0xFF000000, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vpfxs", 0xDC000000, 0xFF000000, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vpfxt", 0xDD000000, 0xFF000000, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vqmul.q", 0xF2808080, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrcp.p", 0xD0100080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrcp.q", 0xD0108080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrcp.s", 0xD0100000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrcp.t", 0xD0108000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrexp2.p",0xD01C0080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrexp2.q",0xD01C8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrexp2.s", 0xD01C0000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrexp2.t",0xD01C8000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf1.p", 0xD0220080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf1.q",0xD0228080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf1.s", 0xD0220000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf1.t",0xD0228000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf2.p", 0xD0230080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf2.q",0xD0238080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf2.s", 0xD0230000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf2.t",0xD0238000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndi.p", 0xD0210080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndi.q", 0xD0218080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndi.s", 0xD0210000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndi.t", 0xD0218000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrnds.s", 0xD0200000, 0xFFFF80FF, "%ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrot.p", 0xF3A00080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrot.q", 0xF3A08080, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrot.t", 0xF3A08000, 0xFFE08080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrsq.p", 0xD0110080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrsq.q", 0xD0118080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrsq.s", 0xD0110000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrsq.t", 0xD0118000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vs2i.p", 0xD03B0080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vs2i.s", 0xD03B0000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat0.p", 0xD0040080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat0.q", 0xD0048080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat0.s", 0xD0040000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat0.t", 0xD0048000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat1.p", 0xD0050080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat1.q", 0xD0058080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat1.s", 0xD0050000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat1.t", 0xD0058000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsbn.s", 0x61000000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsbz.s", 0xD0360000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscl.p", 0x65000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscl.q", 0x65008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscl.t", 0x65008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscmp.p", 0x6E800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscmp.q", 0x6E808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscmp.s", 0x6E800000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscmp.t", 0x6E808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsge.p", 0x6F000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsge.q", 0x6F008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsge.s", 0x6F000000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsge.t", 0x6F008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsgn.p", 0xD04A0080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsgn.q", 0xD04A8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsgn.s", 0xD04A0000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsgn.t", 0xD04A8000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsin.p", 0xD0120080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsin.q", 0xD0128080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsin.s", 0xD0120000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsin.t", 0xD0128000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vslt.p", 0x6F800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vslt.q", 0x6F808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vslt.s", 0x6F800000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vslt.t", 0x6F808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsocp.p", 0xD0450080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsocp.s", 0xD0450000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsqrt.p", 0xD0160080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsqrt.q", 0xD0168080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsqrt.s", 0xD0160000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsqrt.t", 0xD0168000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsrt1.q", 0xD0408080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsrt2.q", 0xD0418080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsrt3.q", 0xD0488080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsrt4.q", 0xD0498080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsub.p", 0x60800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsub.q", 0x60808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsub.s", 0x60800000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsub.t", 0x60808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsync", 0xFFFF0000, 0xFFFF0000, "%I" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsync", 0xFFFF0320, 0xFFFFFFFF, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vt4444.q",0xD0598080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vt5551.q",0xD05A8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vt5650.q",0xD05B8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vtfm2.p", 0xF0800080, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vtfm3.t", 0xF1008000, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vtfm4.q", 0xF1808080, 0xFF808080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vus2i.p", 0xD03A0080, 0xFFFF8080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vus2i.s", 0xD03A0000, 0xFFFF8080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vwb.q", 0xF8000002, 0xFC000002, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vwbn.s", 0xD3000000, 0xFF008080, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vzero.p", 0xD0060080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vzero.q", 0xD0068080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vzero.s", 0xD0060000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vzero.t", 0xD0068000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, +#endif + }; + +static const char *cop0_regs[32] = +{ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "BadVaddr", "Count", NULL, "Compare", "Status", "Cause", "EPC", "PrID", + "Config", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, "EBase", NULL, NULL, "TagLo", "TagHi", "ErrorPC", NULL +}; + +static const char *dr_regs[16] = +{ + "DRCNTL", "DEPC", "DDATA0", "DDATA1", "IBC", "DBC", NULL, NULL, + "IBA", "IBAM", NULL, NULL, "DBA", "DBAM", "DBD", "DBDM" +}; + +static int parse_regnum(const char *reg, int max) +{ + char *endp; + int ret; + + ret = strtoul(reg, &endp, 10); + if((endp != reg) && (*endp == 0) && (ret >= 0) && (ret < max)) + { + return ret; + } + + return -1; +} + +static int parse_gpr(const char *reg, int max) +{ + int ret = -1; + + if(reg[0] == '$') + { + int i; + for(i = 0; i < 32; i++) + { + if(strcasecmp(®[1], regName[i]) == 0) + { + ret = i; + break; + } + } + + if(ret < 0) + { + ret = parse_regnum(®[1], 32); + } + } + else if(reg[0] == 'r') + { + ret = parse_regnum(®[1], 32); + } + else + { + ret = -1; + } + + return ret; +} + +static struct Instruction *find_inst(const char *op, int argcount) +{ + size_t i; + + for(i = 0; i < (sizeof(macro)/sizeof(struct Instruction)); i++) + { + if(strcasecmp(macro[i].name, op) == 0) + { + if(macro[i].operands == argcount) + { + return ¯o[i]; + } + } + } + + for(i = 0; i < (sizeof(g_inst)/sizeof(struct Instruction)); i++) + { + if(strcasecmp(g_inst[i].name, op) == 0) + { + if(g_inst[i].operands == argcount) + { + return &g_inst[i]; + } + } + } + + return NULL; +} + +static char* strip_wsp(char *buf) +{ + int i; + char *ret = buf; + + /* First strip wsp at end */ + + for(i = strlen(buf)-1; i >= 0; i--) + { + if(isspace(buf[i])) + { + buf[i] = 0; + } + else + { + break; + } + } + + /* Now wsp at start */ + while(isspace(*ret)) + { + ret++; + } + + if(ret[0] == 0) + { + return NULL; + } + + return ret; +} + +static int set_imm(const char *imm, unsigned int *opcode, int sign) +{ + int val; + char *endp; + int ret = -1; + + val = strtol(imm, &endp, 0); + if((endp != imm) && (*endp == 0)) + { + if(sign) + { + if(val < SHRT_MIN) + { + fprintf(stderr, "Warning: Signed 16bit immediate underflow in %s\n", imm); + val = SHRT_MIN; + } + + if(val > SHRT_MAX) + { + fprintf(stderr, "Warning: Signed 16bit immediate overflow in %s\n", imm); + val = SHRT_MAX; + } + } + else + { + if(val & 0xFFFF0000) + { + fprintf(stderr, "Warning: Unsigned 16bit immediate overflow in %s\n", imm); + } + } + + *opcode |= (val & 0xFFFF); + ret = 0; + } + else + { + fprintf(stderr, "Invalid immediate %s\n", imm); + } + + return ret; +} + +static int set_gpr(const char *reg, unsigned int *opcode, char type) +{ + int regval; + + regval = parse_gpr(reg, 32); + if(regval < 0) + { + fprintf(stderr, "Invalid GPR %s\n", reg); + return -1; + } + + switch(type) + { + case 'd': SETRD(*opcode, regval); + break; + case 's': SETRS(*opcode, regval); + break; + case 't': SETRT(*opcode, regval); + break; + default: fprintf(stderr, "Internal error, no matching register type\n"); + return -1; + }; + + return 0; +} + +static int set_cop0(const char *reg, unsigned int *opcode) +{ + int i; + int regval = -1; + + if(reg[0] == '$') + { + regval = parse_regnum(®[1], 32); + } + else + { + for(i = 0; i < 32; i++) + { + if(cop0_regs[i]) + { + if(strcasecmp(cop0_regs[i], reg) == 0) + { + regval = i; + break; + } + } + } + } + + if(regval < 0) + { + fprintf(stderr, "Invalid cop0 register %s\n", reg); + return -1; + } + + SETRD(*opcode, regval); + + return 0; +} + +static int set_dr(const char *reg, unsigned int *opcode) +{ + int i; + int regval = -1; + + if(reg[0] == '$') + { + regval = parse_regnum(®[1], 16); + } + else + { + for(i = 0; i < 16; i++) + { + if(dr_regs[i]) + { + if(strcasecmp(cop0_regs[i], reg) == 0) + { + regval = i; + break; + } + } + } + } + + if(regval < 0) + { + fprintf(stderr, "Invalid debug register %s\n", reg); + return -1; + } + + SETRD(*opcode, regval); + + return 0; +} + +static int set_sa(const char *sa, unsigned int *opcode) +{ + int val = -1; + + val = parse_regnum(sa, 32); + + if(val < 0) + { + fprintf(stderr, "Invalid SA value %s\n", sa); + return -1; + } + + SETSA(*opcode, val); + + return 0; +} + +static int set_n(const char *n, unsigned int *opcode) +{ + int val = -1; + + val = parse_regnum(n, 33); + + if(val <= 0) + { + fprintf(stderr, "Invalid N value %s\n", n); + return -1; + } + + SETRD(*opcode, val-1); + + return 0; +} + +static int set_k(const char *n, unsigned int *opcode) +{ + int val = -1; + + val = parse_regnum(n, 32); + + if(val < 0) + { + fprintf(stderr, "Invalid cache value %s\n", n); + return -1; + } + + SETRT(*opcode, val); + + return 0; +} + +static int set_regofs(const char *ofs, unsigned int *opcode) +{ + char buffer[128]; + char *imm; + char *reg; + char *end; + int len; + int ret = -1; + + do + { + len = snprintf(buffer, sizeof(buffer), "%s", ofs); + if((len < 0) || (len >= (int) sizeof(buffer))) + { + fprintf(stderr, "Could not copy offset\n"); + break; + } + + imm = buffer; + reg = strchr(buffer, '('); + if(reg == NULL) + { + fprintf(stderr, "Invalid reg offset format %s (no starting bracket)\n", ofs); + break; + } + + *reg = 0; + reg++; + + end = strchr(reg, ')'); + if(end == NULL) + { + fprintf(stderr, "Invalid reg offset format %s (no ending bracket)\n", ofs); + break; + } + + *end = 0; + + imm = strip_wsp(imm); + if(imm == NULL) + { + fprintf(stderr, "Missing immediate value %s\n", ofs); + break; + } + + reg = strip_wsp(reg); + if(reg == NULL) + { + fprintf(stderr, "Missing register value %s\n", ofs); + break; + } + + if(set_imm(imm, opcode, 1) < 0) + { + break; + } + + if(set_gpr(reg, opcode, 's') < 0) + { + break; + } + + ret = 0; + } + while(0); + + return ret; +} + +static int set_pcofs(const char *jump, unsigned int PC, unsigned int *opcode) +{ + unsigned int addr; + char *endp; + int ret = -1; + + addr = strtoul(jump, &endp, 0); + if((endp != jump) && (*endp == 0)) + { + int ofs; + + ofs = addr - PC - 4; + ofs /= 4; + if(ofs < SHRT_MIN) + { + fprintf(stderr, "Warning: Signed 16bit immediate underflow in %s\n", jump); + ofs = SHRT_MIN; + } + + if(ofs > SHRT_MAX) + { + fprintf(stderr, "Warning: Signed 16bit immediate overflow in %s\n", jump); + ofs = SHRT_MAX; + } + + *opcode |= (ofs & 0xFFFF); + + ret = 0; + } + else + { + fprintf(stderr, "Invalid branch address %s\n", jump); + } + + return ret; +} + +static int set_jump(const char *jump, unsigned int *opcode) +{ + unsigned int addr; + char *endp; + int ret = -1; + + addr = strtoul(jump, &endp, 0); + if((endp != jump) && (*endp == 0)) + { + addr = (addr & 0x0FFFFFFF) >> 2; + *opcode |= addr; + ret = 0; + } + else + { + fprintf(stderr, "Invalid jump address %s\n", jump); + } + + return ret; +} + +static int encode_args(int argcount, char **args, const char *fmt, unsigned int PC, unsigned int *opcode) +{ + int i = 0; + //int vmmul = 0; + int argpos = 0; + int error = 0; + + while(fmt[i]) + { + if(fmt[i] == '%') + { + if(argpos == argcount) + { + fprintf(stderr, "Not enough operands passed for instruction\n"); + goto end; + } + + i++; + switch(fmt[i]) + { + case 'd': + case 't': + case 's': if(set_gpr(args[argpos], opcode, fmt[i]) < 0) + { + error = 1; + break; + } + break; + case 'i': if(set_imm(args[argpos], opcode, 1) < 0) + { + error = 1; + } + break; + case 'I': if(set_imm(args[argpos], opcode, 0) < 0) + { + error = 1; + break; + } + break; + case 'o': if(set_regofs(args[argpos], opcode) < 0) + { + error = 1; + break; + } + break; + case 'O': if(set_pcofs(args[argpos], PC, opcode) < 0) + { + error = 1; + break; + } + break; + case 'j': if(set_jump(args[argpos], opcode) < 0) + { + error = 1; + break; + } + break; + case 'a': if(set_sa(args[argpos], opcode) < 0) + { + error = 1; + break; + } + break; + case '0': if(set_cop0(args[argpos], opcode) < 0) + { + error = 1; + break; + } + break; + case 'n': if(set_n(args[argpos], opcode) < 0) + { + error = 1; + break; + } + break; + case 'k': if(set_k(args[argpos], opcode) < 0) + { + error = 1; + break; + } + break; + case 'r': if(set_dr(args[argpos], opcode) < 0) + { + error = 1; + break; + } + break; +#if 0 + case '1': //output = print_cop1(RD(opcode), output); + break; + case 'p': //*output++ = '$'; + //output = print_int(RD(opcode), output); + break; + case 'D': //output = print_fpureg(FD(opcode), output); + break; + case 'T': //output = print_fpureg(FT(opcode), output); + break; + case 'S': //output = print_fpureg(FS(opcode), output); + break; + case 'x': //if(fmt[i+1]) { output = print_vfpureg(VT(opcode), fmt[i+1], output); i++; } + break; + case 'y': //if(fmt[i+1]) { + // int reg = VS(opcode); + // if(vmmul) { if(reg & 0x20) { reg &= 0x5F; } else { reg |= 0x20; } } + // output = print_vfpureg(reg, fmt[i+1], output); i++; + // } + break; + case 'z': //if(fmt[i+1]) { output = print_vfpureg(VD(opcode), fmt[i+1], output); i++; } + break; + case 'v': break; + case 'X': //if(fmt[i+1]) { output = print_vfpureg(VO(opcode), fmt[i+1], output); i++; } + break; + case 'Z': //output = print_imm(VCC(opcode), output); + break; + case 'c': //output = print_hex(CODE(opcode), output); + break; + case 'C': //output = print_syscall(CODE(opcode), output); + break; + case 'Y': //output = print_ofs(IMM(opcode) & ~3, RS(opcode), output, realregs); + break; + case '?': //vmmul = 1; + break; +#endif + case 0: goto end; + default: fprintf(stderr, "Unsupported operand type %c\n", fmt[i]); + error = 1; + break; + }; + + if(error) + { + break; + } + argpos++; + } + i++; + } + +end: + + if(error) + { + return -1; + } + + return 0; +} + +static int assemble_op(const char *op, int argcount, char **args, unsigned int PC, unsigned int *inst) +{ + struct Instruction *set; + + set = find_inst(op, argcount); + if(set == NULL) + { + fprintf(stderr, "Could not find matching instruction for %s (operands %d)\n", op, argcount); + return -1; + } + + *inst = set->opcode; + + return encode_args(argcount, args, set->fmt, PC, inst); +} + +int asmAssemble(const char *str, unsigned int PC, unsigned int *inst) +{ + char buf[1024]; + int ret; + char *op = NULL; + char *arghead = NULL; + int argcount = 0; + char *args[32]; + + ret = snprintf(buf, sizeof(buf), "%s", str); + if((ret < 0) || (ret >= (int) sizeof(buf))) + { + fprintf(stderr, "Error copying assembler string\n"); + return -1; + } + + /* Split up line, start with the mnemonic, then arguments */ + op = strtok(buf, " \t\r\n"); + if(op == NULL) + { + fprintf(stderr, "Could not find instruction\n"); + return -1; + } + arghead = strtok(NULL, ""); + if(arghead) + { + char *tok; + + /* Remove leading whitespace */ + while(isspace(*arghead)) + { + arghead++; + } + + argcount = 0; + tok = strtok(arghead, ","); + while((tok) && (argcount < 32)) + { + tok = strip_wsp(tok); + if(tok == NULL) + { + fprintf(stderr, "Invalid empty argument\n"); + return -1; + } + args[argcount++] = tok; + tok = strtok(NULL, ","); + } + } + + return assemble_op(op, argcount, args, PC, inst); +} diff --git a/pspsh/asm.h b/pspsh/asm.h new file mode 100644 index 0000000..a4c7539 --- /dev/null +++ b/pspsh/asm.h @@ -0,0 +1,18 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * asm.h - PSPLINK pc terminal simple MIPS assembler + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/pspsh/asm.h $ + * $Id: asm.h 2157 2007-01-29 22:50:26Z tyranid $ + */ +#ifndef __ASM_H__ +#define __ASM_H__ + +int asmAssemble(const char *str, unsigned int PC, unsigned int *inst); + +#endif diff --git a/pspsh/asm.o b/pspsh/asm.o new file mode 100644 index 0000000..053fa4b Binary files /dev/null and b/pspsh/asm.o differ diff --git a/pspsh/disasm.C b/pspsh/disasm.C new file mode 100644 index 0000000..1ad1877 --- /dev/null +++ b/pspsh/disasm.C @@ -0,0 +1,1662 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * disasm.c - PSPLINK pc terminal simple MIPS disassembler + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/pspsh/disasm.C $ + * $Id: disasm.C 2205 2007-03-12 17:34:43Z tyranid $ + */ + +#include +#include +#include "disasm.h" + +/* Format codes + * %d - Rd + * %t - Rt + * %s - Rs + * %i - 16bit signed immediate + * %I - 16bit unsigned immediate (always printed in hex) + * %o - 16bit signed offset (rt base) + * %O - 16bit signed offset (PC relative) + * %V - 16bit signed offset (rs base) + * %j - 26bit absolute offset + * %J - Register jump + * %a - SA + * %0 - Cop0 register + * %1 - Cop1 register + * %2? - Cop2 register (? is (s, d)) + * %p - General cop (i.e. numbered) register + * %n? - ins/ext size, ? (e, i) + * %r - Debug register + * %k - Cache function + * %D - Fd + * %T - Ft + * %S - Fs + * %x? - Vt (? is (s/scalar, p/pair, t/triple, q/quad, m/matrix pair, n/matrix triple, o/matrix quad) + * %y? - Vs + * %z? - Vd + * %X? - Vo (? is (s, q)) + * %Y - VFPU offset + * %Z? - VFPU condition code/name (? is (c, n)) + * %v? - VFPU immediate, ? (3, 5, 8, k, i, h, r, p? (? is (0, 1, 2, 3, 4, 5, 6, 7))) + * %c - code (for break) + * %C - code (for syscall) + * %? - Indicates vmmul special exception + */ + +#define RT(op) ((op >> 16) & 0x1F) +#define RS(op) ((op >> 21) & 0x1F) +#define RD(op) ((op >> 11) & 0x1F) +#define FT(op) ((op >> 16) & 0x1F) +#define FS(op) ((op >> 11) & 0x1F) +#define FD(op) ((op >> 6) & 0x1F) +#define SA(op) ((op >> 6) & 0x1F) +#define IMM(op) ((signed short) (op & 0xFFFF)) +#define IMMU(op) ((unsigned short) (op & 0xFFFF)) +#define JUMP(op, pc) ((pc & 0xF0000000) | ((op & 0x3FFFFFF) << 2)) +#define CODE(op) ((op >> 6) & 0xFFFFF) +#define SIZE(op) ((op >> 11) & 0x1F) +#define POS(op) ((op >> 6) & 0x1F) +#define VO(op) (((op & 3) << 5) | ((op >> 16) & 0x1F)) +#define VCC(op) ((op >> 18) & 7) +#define VD(op) (op & 0x7F) +#define VS(op) ((op >> 8) & 0x7F) +#define VT(op) ((op >> 16) & 0x7F) + +// [hlide] new #defines +#define VED(op) (op & 0xFF) +#define VES(op) ((op >> 8) & 0xFF) +#define VCN(op) (op & 0x0F) +#define VI3(op) ((op >> 16) & 0x07) +#define VI5(op) ((op >> 16) & 0x1F) +#define VI8(op) ((op >> 16) & 0xFF) + +struct Instruction +{ + const char *name; + unsigned int opcode; + unsigned int mask; + const char *fmt; + int addrtype; + int type; +}; + +#define INSTR_TYPE_PSP 1 +#define INSTR_TYPE_B 2 +#define INSTR_TYPE_JUMP 4 +#define INSTR_TYPE_JAL 8 + +#define INSTR_TYPE_BRANCH (INSTR_TYPE_B | INSTR_TYPE_JUMP | INSTR_TYPE_JAL) + +#define ADDR_TYPE_NONE 0 +#define ADDR_TYPE_16 1 +#define ADDR_TYPE_26 2 +#define ADDR_TYPE_REG 3 + +static const char *regName[32] = +{ + "zr", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" +}; + +struct Instruction g_macro[] = +{ + /* Macro instructions */ + { "nop", 0x00000000, 0xFFFFFFFF, "" , ADDR_TYPE_NONE, 0 }, + { "li", 0x24000000, 0xFFE00000, "%t, %i" , ADDR_TYPE_NONE, 0 }, + { "li", 0x34000000, 0xFFE00000, "%t, %I" , ADDR_TYPE_NONE, 0 }, + { "move", 0x00000021, 0xFC1F07FF, "%d, %s" , ADDR_TYPE_NONE, 0 }, + { "move", 0x00000025, 0xFC1F07FF, "%d, %s" , ADDR_TYPE_NONE, 0 }, + { "b", 0x10000000, 0xFFFF0000, "%O" , ADDR_TYPE_16, INSTR_TYPE_B }, + { "b", 0x04010000, 0xFFFF0000, "%O" , ADDR_TYPE_16, INSTR_TYPE_B }, + { "bal", 0x04110000, 0xFFFF0000, "%O" , ADDR_TYPE_16, INSTR_TYPE_JAL }, + { "bnez", 0x14000000, 0xFC1F0000, "%s, %O" , ADDR_TYPE_16, INSTR_TYPE_B }, + { "bnezl", 0x54000000, 0xFC1F0000, "%s, %O" , ADDR_TYPE_16, INSTR_TYPE_B }, + { "beqz", 0x10000000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "beqzl", 0x50000000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "neg", 0x00000022, 0xFFE007FF, "%d, %t" , ADDR_TYPE_NONE, 0 }, + { "negu", 0x00000023, 0xFFE007FF, "%d, %t" , ADDR_TYPE_NONE, 0 }, + { "not", 0x00000027, 0xFC1F07FF, "%d, %s" , ADDR_TYPE_NONE, 0 }, + { "jalr", 0x0000F809, 0xFC1FFFFF, "%J", ADDR_TYPE_REG, INSTR_TYPE_JAL }, +}; + +static struct Instruction g_inst[] = +{ + /* MIPS instructions */ + { "add", 0x00000020, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "addi", 0x20000000, 0xFC000000, "%t, %s, %i", ADDR_TYPE_NONE, 0 }, + { "addiu", 0x24000000, 0xFC000000, "%t, %s, %i", ADDR_TYPE_NONE, 0 }, + { "addu", 0x00000021, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "and", 0x00000024, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "andi", 0x30000000, 0xFC000000, "%t, %s, %I", ADDR_TYPE_NONE, 0 }, + { "beq", 0x10000000, 0xFC000000, "%s, %t, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "beql", 0x50000000, 0xFC000000, "%s, %t, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "bgez", 0x04010000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "bgezal", 0x04110000, 0xFC1F0000, "%s, %0", ADDR_TYPE_16, INSTR_TYPE_JAL }, + { "bgezl", 0x04030000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "bgtz", 0x1C000000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "bgtzl", 0x5C000000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "bitrev", 0x7C000520, 0xFFE007FF, "%d, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "blez", 0x18000000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "blezl", 0x58000000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "bltz", 0x04000000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "bltzl", 0x04020000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "bltzal", 0x04100000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_JAL }, + { "bltzall", 0x04120000, 0xFC1F0000, "%s, %O", ADDR_TYPE_16, INSTR_TYPE_JAL }, + { "bne", 0x14000000, 0xFC000000, "%s, %t, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "bnel", 0x54000000, 0xFC000000, "%s, %t, %O", ADDR_TYPE_16, INSTR_TYPE_B }, + { "break", 0x0000000D, 0xFC00003F, "%c", ADDR_TYPE_NONE, 0 }, + { "cache", 0xbc000000, 0xfc000000, "%k, %o", ADDR_TYPE_NONE, 0 }, + { "cfc0", 0x40400000, 0xFFE007FF, "%t, %p", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "clo", 0x00000017, 0xFC1F07FF, "%d, %s", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "clz", 0x00000016, 0xFC1F07FF, "%d, %s", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "ctc0", 0x40C00000, 0xFFE007FF, "%t, %p", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "max", 0x0000002C, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "min", 0x0000002D, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "dbreak", 0x7000003F, 0xFFFFFFFF, "", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "div", 0x0000001A, 0xFC00FFFF, "%s, %t", ADDR_TYPE_NONE, 0 }, + { "divu", 0x0000001B, 0xFC00FFFF, "%s, %t", ADDR_TYPE_NONE, 0 }, + { "dret", 0x7000003E, 0xFFFFFFFF, "", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "eret", 0x42000018, 0xFFFFFFFF, "", ADDR_TYPE_NONE, 0 }, + { "ext", 0x7C000000, 0xFC00003F, "%t, %s, %a, %ne", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "ins", 0x7C000004, 0xFC00003F, "%t, %s, %a, %ni", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "j", 0x08000000, 0xFC000000, "%j", ADDR_TYPE_26, INSTR_TYPE_JUMP }, + { "jr", 0x00000008, 0xFC1FFFFF, "%J", ADDR_TYPE_REG, INSTR_TYPE_JUMP }, + { "jalr", 0x00000009, 0xFC1F07FF, "%J, %d", ADDR_TYPE_REG, INSTR_TYPE_JAL }, + { "jal", 0x0C000000, 0xFC000000, "%j", ADDR_TYPE_26, INSTR_TYPE_JAL }, + { "lb", 0x80000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "lbu", 0x90000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "lh", 0x84000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "lhu", 0x94000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "ll", 0xC0000000, 0xFC000000, "%t, %O", ADDR_TYPE_NONE, 0 }, + { "lui", 0x3C000000, 0xFFE00000, "%t, %I", ADDR_TYPE_NONE, 0 }, + { "lw", 0x8C000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "lwl", 0x88000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "lwr", 0x98000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "madd", 0x0000001C, 0xFC00FFFF, "%s, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "maddu", 0x0000001D, 0xFC00FFFF, "%s, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mfc0", 0x40000000, 0xFFE007FF, "%t, %0", ADDR_TYPE_NONE, 0 }, + { "mfdr", 0x7000003D, 0xFFE007FF, "%t, %r", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mfhi", 0x00000010, 0xFFFF07FF, "%d", ADDR_TYPE_NONE, 0 }, + { "mfic", 0x70000024, 0xFFE007FF, "%t, %p", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mflo", 0x00000012, 0xFFFF07FF, "%d", ADDR_TYPE_NONE, 0 }, + { "movn", 0x0000000B, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "movz", 0x0000000A, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "msub", 0x0000002e, 0xfc00ffff, "%d, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "msubu", 0x0000002f, 0xfc00ffff, "%d, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mtc0", 0x40800000, 0xFFE007FF, "%t, %0", ADDR_TYPE_NONE, 0 }, + { "mtdr", 0x7080003D, 0xFFE007FF, "%t, %r", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mtic", 0x70000026, 0xFFE007FF, "%t, %p", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "halt", 0x70000000, 0xFFFFFFFF, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mthi", 0x00000011, 0xFC1FFFFF, "%s", ADDR_TYPE_NONE, 0 }, + { "mtlo", 0x00000013, 0xFC1FFFFF, "%s", ADDR_TYPE_NONE, 0 }, + { "mult", 0x00000018, 0xFC00FFFF, "%s, %t", ADDR_TYPE_NONE, 0 }, + { "multu", 0x00000019, 0xFC0007FF, "%s, %t", ADDR_TYPE_NONE, 0 }, + { "nor", 0x00000027, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "or", 0x00000025, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "ori", 0x34000000, 0xFC000000, "%t, %s, %I", ADDR_TYPE_NONE, 0 }, + { "rotr", 0x00200002, 0xFFE0003F, "%d, %t, %a", ADDR_TYPE_NONE, 0 }, + { "rotv", 0x00000046, 0xFC0007FF, "%d, %t, %s", ADDR_TYPE_NONE, 0 }, + { "seb", 0x7C000420, 0xFFE007FF, "%d, %t", ADDR_TYPE_NONE, 0 }, + { "seh", 0x7C000620, 0xFFE007FF, "%d, %t", ADDR_TYPE_NONE, 0 }, + { "sb", 0xA0000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "sh", 0xA4000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "sllv", 0x00000004, 0xFC0007FF, "%d, %t, %s", ADDR_TYPE_NONE, 0 }, + { "sll", 0x00000000, 0xFFE0003F, "%d, %t, %a", ADDR_TYPE_NONE, 0 }, + { "slt", 0x0000002A, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "slti", 0x28000000, 0xFC000000, "%t, %s, %i", ADDR_TYPE_NONE, 0 }, + { "sltiu", 0x2C000000, 0xFC000000, "%t, %s, %i", ADDR_TYPE_NONE, 0 }, + { "sltu", 0x0000002B, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "sra", 0x00000003, 0xFFE0003F, "%d, %t, %a", ADDR_TYPE_NONE, 0 }, + { "srav", 0x00000007, 0xFC0007FF, "%d, %t, %s", ADDR_TYPE_NONE, 0 }, + { "srlv", 0x00000006, 0xFC0007FF, "%d, %t, %s", ADDR_TYPE_NONE, 0 }, + { "srl", 0x00000002, 0xFFE0003F, "%d, %t, %a", ADDR_TYPE_NONE, 0 }, + { "sw", 0xAC000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "swl", 0xA8000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "swr", 0xB8000000, 0xFC000000, "%t, %o", ADDR_TYPE_NONE, 0 }, + { "sub", 0x00000022, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "subu", 0x00000023, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "sync", 0x0000000F, 0xFFFFFFFF, "", ADDR_TYPE_NONE, 0 }, + { "syscall", 0x0000000C, 0xFC00003F, "%C", ADDR_TYPE_NONE, 0 }, + { "xor", 0x00000026, 0xFC0007FF, "%d, %s, %t", ADDR_TYPE_NONE, 0 }, + { "xori", 0x38000000, 0xFC000000, "%t, %s, %I", ADDR_TYPE_NONE, 0 }, + { "wsbh", 0x7C0000A0, 0xFFE007FF, "%d, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "wsbw", 0x7C0000E0, 0xFFE007FF, "%d, %t", ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + + /* FPU instructions */ + {"abs.s", 0x46000005, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"add.s", 0x46000000, 0xFFE0003F, "%D, %S, %T", ADDR_TYPE_NONE, 0 }, + {"bc1f", 0x45000000, 0xFFFF0000, "%O", ADDR_TYPE_16, INSTR_TYPE_B }, + {"bc1fl", 0x45020000, 0xFFFF0000, "%O", ADDR_TYPE_16, INSTR_TYPE_B }, + {"bc1t", 0x45010000, 0xFFFF0000, "%O", ADDR_TYPE_16, INSTR_TYPE_B }, + {"bc1tl", 0x45030000, 0xFFFF0000, "%O", ADDR_TYPE_16, INSTR_TYPE_B }, + {"c.f.s", 0x46000030, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.un.s", 0x46000031, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.eq.s", 0x46000032, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ueq.s", 0x46000033, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.olt.s", 0x46000034, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ult.s", 0x46000035, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ole.s", 0x46000036, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ule.s", 0x46000037, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.sf.s", 0x46000038, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ngle.s",0x46000039, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.seq.s", 0x4600003A, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ngl.s", 0x4600003B, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.lt.s", 0x4600003C, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.nge.s", 0x4600003D, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.le.s", 0x4600003E, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"c.ngt.s", 0x4600003F, 0xFFE007FF, "%S, %T", ADDR_TYPE_NONE, 0 }, + {"ceil.w.s",0x4600000E, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"cfc1", 0x44400000, 0xFFE007FF, "%t, %p", ADDR_TYPE_NONE, 0 }, + {"ctc1", 0x44c00000, 0xFFE007FF, "%t, %p", ADDR_TYPE_NONE, 0 }, + {"cvt.s.w", 0x46800020, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"cvt.w.s", 0x46000024, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"div.s", 0x46000003, 0xFFE0003F, "%D, %S, %T", ADDR_TYPE_NONE, 0 }, + {"floor.w.s",0x4600000F, 0xFFFF003F,"%D, %S", ADDR_TYPE_NONE, 0 }, + {"lwc1", 0xc4000000, 0xFC000000, "%T, %o", ADDR_TYPE_NONE, 0 }, + {"mfc1", 0x44000000, 0xFFE007FF, "%t, %1", ADDR_TYPE_NONE, 0 }, + {"mov.s", 0x46000006, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"mtc1", 0x44800000, 0xFFE007FF, "%t, %1", ADDR_TYPE_NONE, 0 }, + {"mul.s", 0x46000002, 0xFFE0003F, "%D, %S, %T", ADDR_TYPE_NONE, 0 }, + {"neg.s", 0x46000007, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"round.w.s",0x4600000C, 0xFFFF003F,"%D, %S", ADDR_TYPE_NONE, 0 }, + {"sqrt.s", 0x46000004, 0xFFFF003F, "%D, %S", ADDR_TYPE_NONE, 0 }, + {"sub.s", 0x46000001, 0xFFE0003F, "%D, %S, %T", ADDR_TYPE_NONE, 0 }, + {"swc1", 0xe4000000, 0xFC000000, "%T, %o", ADDR_TYPE_NONE, 0 }, + {"trunc.w.s",0x4600000D, 0xFFFF003F,"%D, %S", ADDR_TYPE_NONE, 0 }, + + /* VPU instructions */ + { "bvf", 0x49000000, 0xFFE30000, "%Zc, %O" , ADDR_TYPE_16, INSTR_TYPE_PSP | INSTR_TYPE_B }, // [hlide] %Z -> %Zc + { "bvfl", 0x49020000, 0xFFE30000, "%Zc, %O" , ADDR_TYPE_16, INSTR_TYPE_PSP | INSTR_TYPE_B }, // [hlide] %Z -> %Zc + { "bvt", 0x49010000, 0xFFE30000, "%Zc, %O" , ADDR_TYPE_16, INSTR_TYPE_PSP | INSTR_TYPE_B }, // [hlide] %Z -> %Zc + { "bvtl", 0x49030000, 0xFFE30000, "%Zc, %O" , ADDR_TYPE_16, INSTR_TYPE_PSP | INSTR_TYPE_B }, // [hlide] %Z -> %Zc + { "lv.q", 0xD8000000, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "lv.s", 0xC8000000, 0xFC000000, "%Xs, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "lvl.q", 0xD4000000, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "lvr.q", 0xD4000002, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mfv", 0x48600000, 0xFFE0FF80, "%t, %zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%t, %zs" + { "mfvc", 0x48600000, 0xFFE0FF00, "%t, %2d" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%t, %2d" + { "mtv", 0x48E00000, 0xFFE0FF80, "%t, %zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%t, %zs" + { "mtvc", 0x48E00000, 0xFFE0FF00, "%t, %2d" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%t, %2d" + { "sv.q", 0xF8000000, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "sv.s", 0xE8000000, 0xFC000000, "%Xs, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "svl.q", 0xF4000000, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "svr.q", 0xF4000002, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vabs.p", 0xD0010080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vabs.q", 0xD0018080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vabs.s", 0xD0010000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vabs.t", 0xD0018000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vadd.p", 0x60000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vadd.q", 0x60008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vadd.s", 0x60000000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %yz -> %ys + { "vadd.t", 0x60008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vasin.p", 0xD0170080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vasin.q", 0xD0178080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vasin.s", 0xD0170000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vasin.t", 0xD0178000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vavg.p", 0xD0470080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vavg.q", 0xD0478080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vavg.t", 0xD0478000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vbfy1.p", 0xD0420080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vbfy1.q", 0xD0428080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vbfy2.q", 0xD0438080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcmovf.p", 0xD2A80080, 0xFFF88080, "%zp, %yp, %v3" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %yp, %v3" + { "vcmovf.q",0xD2A88080, 0xFFF88080, "%zq, %yq, %v3" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yq, %v3" + { "vcmovf.s", 0xD2A80000, 0xFFF88080, "%zs, %ys, %v3" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %ys, %v3" + { "vcmovf.t",0xD2A88000, 0xFFF88080, "%zt, %yt, %v3" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %yt, %v3" + { "vcmovt.p", 0xD2A00080, 0xFFF88080, "%zp, %yp, %v3" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %yp, %v3" + { "vcmovt.q",0xD2A08080, 0xFFF88080, "%zq, %yq, %v3" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yq, %v3" + { "vcmovt.s", 0xD2A00000, 0xFFF88080, "%zs, %ys, %v3" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %ys, %v3" + { "vcmovt.t",0xD2A08000, 0xFFF88080, "%zt, %yt, %v3" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %yt, %v3" + { "vcmp.p", 0x6C000080, 0xFF8080F0, "%Zn, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn, %zp, %xp" + { "vcmp.p", 0x6C000080, 0xFFFF80F0, "%Zn, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn, %xp" + { "vcmp.p", 0x6C000080, 0xFFFFFFF0, "%Zn" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn" + { "vcmp.q", 0x6C008080, 0xFF8080F0, "%Zn, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn, %yq, %xq" + { "vcmp.q", 0x6C008080, 0xFFFF80F0, "%Zn, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn, %yq" + { "vcmp.q", 0x6C008080, 0xFFFFFFF0, "%Zn" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn" + { "vcmp.s", 0x6C000000, 0xFF8080F0, "%Zn, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn, %ys, %xs" + { "vcmp.s", 0x6C000000, 0xFFFF80F0, "%Zn, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn, %ys" + { "vcmp.s", 0x6C000000, 0xFFFFFFF0, "%Zn" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn" + { "vcmp.t", 0x6C008000, 0xFF8080F0, "%Zn, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn, %yt, %xt" + { "vcmp.t", 0x6C008000, 0xFFFF80F0, "%Zn, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%Zn, %yt" + { "vcmp.t", 0x6C008000, 0xFFFFFFF0, "%Zn" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp" + { "vcos.p", 0xD0130080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcos.q", 0xD0138080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcos.s", 0xD0130000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcos.t", 0xD0138000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcrs.t", 0x66808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcrsp.t", 0xF2808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vcst.p", 0xD0600080, 0xFFE0FF80, "%zp, %vk" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] "%zp, %yp, %xp" -> "%zp, %vk" + { "vcst.q", 0xD0608080, 0xFFE0FF80, "%zq, %vk" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] "%zq, %yq, %xq" -> "%zq, %vk" + { "vcst.s", 0xD0600000, 0xFFE0FF80, "%zs, %vk" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] "%zs, %ys, %xs" -> "%zs, %vk" + { "vcst.t", 0xD0608000, 0xFFE0FF80, "%zt, %vk" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] "%zt, %yt, %xt" -> "%zt, %vk" + { "vdet.p", 0x67000080, 0xFF808080, "%zs, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdiv.p", 0x63800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdiv.q", 0x63808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdiv.s", 0x63800000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %yz -> %ys + { "vdiv.t", 0x63808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdot.p", 0x64800080, 0xFF808080, "%zs, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdot.q", 0x64808080, 0xFF808080, "%zs, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vdot.t", 0x64808000, 0xFF808080, "%zs, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vexp2.p", 0xD0140080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vexp2.q", 0xD0148080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vexp2.s", 0xD0140000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vexp2.t", 0xD0148000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vf2h.p", 0xD0320080, 0xFFFF8080, "%zs, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp -> %zs + { "vf2h.q", 0xD0328080, 0xFFFF8080, "%zp, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zq -> %zp + { "vf2id.p", 0xD2600080, 0xFFE08080, "%zp, %yp, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %yp, %v5" + { "vf2id.q", 0xD2608080, 0xFFE08080, "%zq, %yq, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yq, %v5" + { "vf2id.s", 0xD2600000, 0xFFE08080, "%zs, %ys, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %ys, %v5" + { "vf2id.t", 0xD2608000, 0xFFE08080, "%zt, %yt, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %yt, %v5" + { "vf2in.p", 0xD2000080, 0xFFE08080, "%zp, %yp, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %yp, %v5" + { "vf2in.q", 0xD2008080, 0xFFE08080, "%zq, %yq, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yq, %v5" + { "vf2in.s", 0xD2000000, 0xFFE08080, "%zs, %ys, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %ys, %v5" + { "vf2in.t", 0xD2008000, 0xFFE08080, "%zt, %yt, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %yt, %v5" + { "vf2iu.p", 0xD2400080, 0xFFE08080, "%zp, %yp, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %yp, %v5" + { "vf2iu.q", 0xD2408080, 0xFFE08080, "%zq, %yq, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yq, %v5" + { "vf2iu.s", 0xD2400000, 0xFFE08080, "%zs, %ys, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %ys, %v5" + { "vf2iu.t", 0xD2408000, 0xFFE08080, "%zt, %yt, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %yt, %v5" + { "vf2iz.p", 0xD2200080, 0xFFE08080, "%zp, %yp, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %yp, %v5" + { "vf2iz.q", 0xD2208080, 0xFFE08080, "%zq, %yq, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yq, %v5" + { "vf2iz.s", 0xD2200000, 0xFFE08080, "%zs, %ys, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %ys, %v5" + { "vf2iz.t", 0xD2208000, 0xFFE08080, "%zt, %yt, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %yt, %v5" + { "vfad.p", 0xD0460080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vfad.q", 0xD0468080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vfad.t", 0xD0468000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vfim.s", 0xDF800000, 0xFF800000, "%xs, %vh" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%xs, %vh" + { "vflush", 0xFFFF040D, 0xFFFFFFFF, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vh2f.p", 0xD0330080, 0xFFFF8080, "%zq, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp -> %zq + { "vh2f.s", 0xD0330000, 0xFFFF8080, "%zp, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zs -> %zp + { "vhdp.p", 0x66000080, 0xFF808080, "%zs, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %yp, %xp" + { "vhdp.q", 0x66008080, 0xFF808080, "%zs, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %yq, %xq" + { "vhdp.t", 0x66008000, 0xFF808080, "%zs, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %yt, %xt" + { "vhtfm2.p", 0xF0800000, 0xFF808080, "%zp, %ym, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %ym, %xp" + { "vhtfm3.t",0xF1000080, 0xFF808080, "%zt, %yn, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %yn, %xt" + { "vhtfm4.q",0xF1808000, 0xFF808080, "%zq, %yo, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yo, %xq" + { "vi2c.q", 0xD03D8080, 0xFFFF8080, "%zs, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %yq" + { "vi2f.p", 0xD2800080, 0xFFE08080, "%zp, %yp, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %yp, %v5" + { "vi2f.q", 0xD2808080, 0xFFE08080, "%zq, %yq, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yq, %v5" + { "vi2f.s", 0xD2800000, 0xFFE08080, "%zs, %ys, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %ys, %v5" + { "vi2f.t", 0xD2808000, 0xFFE08080, "%zt, %yt, %v5" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %yt, %v5" + { "vi2s.p", 0xD03F0080, 0xFFFF8080, "%zs, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %yp" + { "vi2s.q", 0xD03F8080, 0xFFFF8080, "%zp, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %yq" + { "vi2uc.q", 0xD03C8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp -> %zq + { "vi2us.p", 0xD03E0080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp -> %zq + { "vi2us.q", 0xD03E8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp -> %zq + { "vidt.p", 0xD0030080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vidt.q", 0xD0038080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "viim.s", 0xDF000000, 0xFF800000, "%xs, %vi" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%xs, %vi" + { "vlgb.s", 0xD0370000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vlog2.p", 0xD0150080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vlog2.q", 0xD0158080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vlog2.s", 0xD0150000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vlog2.t", 0xD0158000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmax.p", 0x6D800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmax.q", 0x6D808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmax.s", 0x6D800000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmax.t", 0x6D808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmfvc", 0xD0500000, 0xFFFF0080, "%zs, %2s" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zs, %2s" + { "vmidt.p", 0xF3830080, 0xFFFFFF80, "%zm" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp -> %zm + { "vmidt.q", 0xF3838080, 0xFFFFFF80, "%zo" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zq -> %zo + { "vmidt.t", 0xF3838000, 0xFFFFFF80, "%zn" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zt -> %zn + { "vmin.p", 0x6D000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmin.q", 0x6D008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmin.s", 0x6D000000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmin.t", 0x6D008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmmov.p", 0xF3800080, 0xFFFF8080, "%zm, %ym" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zm, %ym" + { "vmmov.q", 0xF3808080, 0xFFFF8080, "%zo, %yo" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmmov.t", 0xF3808000, 0xFFFF8080, "%zn, %yn" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zn, %yn" + { "vmmul.p", 0xF0000080, 0xFF808080, "%?%zm, %ym, %xm" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%?%zm, %ym, %xm" + { "vmmul.q", 0xF0008080, 0xFF808080, "%?%zo, %yo, %xo" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmmul.t", 0xF0008000, 0xFF808080, "%?%zn, %yn, %xn" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%?%zn, %yn, %xn" + { "vmone.p", 0xF3870080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmone.q", 0xF3878080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmone.t", 0xF3878000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmov.p", 0xD0000080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmov.q", 0xD0008080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmov.s", 0xD0000000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmov.t", 0xD0008000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmscl.p", 0xF2000080, 0xFF808080, "%zm, %ym, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp, %yp, %xp -> %zm, %ym, %xs + { "vmscl.q", 0xF2008080, 0xFF808080, "%zo, %yo, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zq, %yq, %xp -> %zo, %yo, %xs + { "vmscl.t", 0xF2008000, 0xFF808080, "%zn, %yn, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zt, %yt, %xp -> %zn, %yn, %xs + { "vmtvc", 0xD0510000, 0xFFFF8000, "%2d, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%2d, %ys" + { "vmul.p", 0x64000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmul.q", 0x64008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmul.s", 0x64000000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmul.t", 0x64008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vmzero.p", 0xF3860080, 0xFFFFFF80, "%zm" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp -> %zm + { "vmzero.q",0xF3868080, 0xFFFFFF80, "%zo" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zq -> %zo + { "vmzero.t",0xF3868000, 0xFFFFFF80, "%zn" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zt -> %zn + { "vneg.p", 0xD0020080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vneg.q", 0xD0028080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vneg.s", 0xD0020000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vneg.t", 0xD0028000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnop", 0xFFFF0000, 0xFFFFFFFF, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnrcp.p", 0xD0180080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnrcp.q", 0xD0188080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnrcp.s", 0xD0180000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnrcp.t", 0xD0188000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnsin.p", 0xD01A0080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnsin.q", 0xD01A8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnsin.s", 0xD01A0000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vnsin.t", 0xD01A8000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vocp.p", 0xD0440080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vocp.q", 0xD0448080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vocp.s", 0xD0440000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vocp.t", 0xD0448000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vone.p", 0xD0070080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vone.q", 0xD0078080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vone.s", 0xD0070000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vone.t", 0xD0078000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vpfxd", 0xDE000000, 0xFF000000, "[%vp4, %vp5, %vp6, %vp7]" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "[%vp4, %vp5, %vp6, %vp7]" + { "vpfxs", 0xDC000000, 0xFF000000, "[%vp0, %vp1, %vp2, %vp3]" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "[%vp0, %vp1, %vp2, %vp3]" + { "vpfxt", 0xDD000000, 0xFF000000, "[%vp0, %vp1, %vp2, %vp3]" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "[%vp0, %vp1, %vp2, %vp3]" + { "vqmul.q", 0xF2808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yq, %xq" + { "vrcp.p", 0xD0100080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrcp.q", 0xD0108080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrcp.s", 0xD0100000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrcp.t", 0xD0108000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrexp2.p",0xD01C0080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrexp2.q",0xD01C8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrexp2.s", 0xD01C0000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrexp2.t",0xD01C8000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf1.p", 0xD0220080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf1.q",0xD0228080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf1.s", 0xD0220000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf1.t",0xD0228000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf2.p", 0xD0230080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf2.q",0xD0238080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf2.s", 0xD0230000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndf2.t",0xD0238000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndi.p", 0xD0210080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndi.q", 0xD0218080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndi.s", 0xD0210000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrndi.t", 0xD0218000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrnds.s", 0xD0200000, 0xFFFF80FF, "%ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrot.p", 0xF3A00080, 0xFFE08080, "%zp, %ys, %vr" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %ys, %vr" + { "vrot.q", 0xF3A08080, 0xFFE08080, "%zq, %ys, %vr" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %ys, %vr" + { "vrot.t", 0xF3A08000, 0xFFE08080, "%zt, %ys, %vr" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %ys, %vr" + { "vrsq.p", 0xD0110080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrsq.q", 0xD0118080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrsq.s", 0xD0110000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vrsq.t", 0xD0118000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vs2i.p", 0xD03B0080, 0xFFFF8080, "%zq, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp -> %zq + { "vs2i.s", 0xD03B0000, 0xFFFF8080, "%zp, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zs -> %zp + { "vsat0.p", 0xD0040080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat0.q", 0xD0048080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat0.s", 0xD0040000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat0.t", 0xD0048000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat1.p", 0xD0050080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat1.q", 0xD0058080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat1.s", 0xD0050000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsat1.t", 0xD0058000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsbn.s", 0x61000000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsbz.s", 0xD0360000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscl.p", 0x65000080, 0xFF808080, "%zp, %yp, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %xp -> %xs + { "vscl.q", 0x65008080, 0xFF808080, "%zq, %yq, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %xq -> %xs + { "vscl.t", 0x65008000, 0xFF808080, "%zt, %yt, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %xt -> %xs + { "vscmp.p", 0x6E800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscmp.q", 0x6E808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscmp.s", 0x6E800000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vscmp.t", 0x6E808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsge.p", 0x6F000080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsge.q", 0x6F008080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsge.s", 0x6F000000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsge.t", 0x6F008000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsgn.p", 0xD04A0080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsgn.q", 0xD04A8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsgn.s", 0xD04A0000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsgn.t", 0xD04A8000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsin.p", 0xD0120080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsin.q", 0xD0128080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsin.s", 0xD0120000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsin.t", 0xD0128000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vslt.p", 0x6F800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vslt.q", 0x6F808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vslt.s", 0x6F800000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vslt.t", 0x6F808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsocp.p", 0xD0450080, 0xFFFF8080, "%zq, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zp -> %zq + { "vsocp.s", 0xD0450000, 0xFFFF8080, "%zp, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zs -> %zp + { "vsqrt.p", 0xD0160080, 0xFFFF8080, "%zp, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsqrt.q", 0xD0168080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsqrt.s", 0xD0160000, 0xFFFF8080, "%zs, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsqrt.t", 0xD0168000, 0xFFFF8080, "%zt, %yt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsrt1.q", 0xD0408080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsrt2.q", 0xD0418080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsrt3.q", 0xD0488080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsrt4.q", 0xD0498080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsub.p", 0x60800080, 0xFF808080, "%zp, %yp, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsub.q", 0x60808080, 0xFF808080, "%zq, %yq, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsub.s", 0x60800000, 0xFF808080, "%zs, %ys, %xs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsub.t", 0x60808000, 0xFF808080, "%zt, %yt, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsync", 0xFFFF0000, 0xFFFF0000, "%I" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vsync", 0xFFFF0320, 0xFFFFFFFF, "" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vt4444.q",0xD0598080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zq -> %zp + { "vt5551.q",0xD05A8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zq -> %zp + { "vt5650.q",0xD05B8080, 0xFFFF8080, "%zq, %yq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] %zq -> %zp + { "vtfm2.p", 0xF0800080, 0xFF808080, "%zp, %ym, %xp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %ym, %xp" + { "vtfm3.t", 0xF1008000, 0xFF808080, "%zt, %yn, %xt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zt, %yn, %xt" + { "vtfm4.q", 0xF1808080, 0xFF808080, "%zq, %yo, %xq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yo, %xq" + { "vus2i.p", 0xD03A0080, 0xFFFF8080, "%zq, %yp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zq, %yp" + { "vus2i.s", 0xD03A0000, 0xFFFF8080, "%zp, %ys" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, // [hlide] added "%zp, %ys" + { "vwb.q", 0xF8000002, 0xFC000002, "%Xq, %Y" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vwbn.s", 0xD3000000, 0xFF008080, "%zs, %xs, %I" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vzero.p", 0xD0060080, 0xFFFFFF80, "%zp" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vzero.q", 0xD0068080, 0xFFFFFF80, "%zq" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vzero.s", 0xD0060000, 0xFFFFFF80, "%zs" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "vzero.t", 0xD0068000, 0xFFFFFF80, "%zt" , ADDR_TYPE_NONE, INSTR_TYPE_PSP }, + { "mfvme", 0x68000000, 0xFC000000, "%t, %i", ADDR_TYPE_NONE, 0 }, + { "mtvme", 0xb0000000, 0xFC000000, "%t, %i", ADDR_TYPE_NONE, 0 }, + + }; + +extern const char *regName[32]; + +static const char *cop0_regs[32] = +{ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "BadVaddr", "Count", NULL, "Compare", "Status", "Cause", "EPC", "PrID", + "Config", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, "EBase", NULL, NULL, "TagLo", "TagHi", "ErrorPC", NULL +}; + +static const char * dr_regs[16] = +{ + "DRCNTL", "DEPC", "DDATA0", "DDATA1", "IBC", "DBC", NULL, NULL, + "IBA", "IBAM", NULL, NULL, "DBA", "DBAM", "DBD", "DBDM" +}; + +/* TODO: Add a register state block so we can convert lui/addiu to li */ + +static int g_hexints = 0; +static int g_mregs = 0; +static int g_symaddr = 0; +static int g_macroon = 0; +static int g_printreal = 0; +static int g_printregs = 0; +static int g_regmask = 0; +static int g_printswap = 0; +static int g_signedhex = 0; +static SymbolMap *g_syms = NULL; + +struct DisasmOpt +{ + char opt; + int *value; + const char *name; +}; + +struct DisasmOpt g_disopts[DISASM_OPT_MAX] = { + { DISASM_OPT_HEXINTS, &g_hexints, "Hex Integers" }, + { DISASM_OPT_MREGS, &g_mregs, "Mnemonic Registers" }, + { DISASM_OPT_SYMADDR, &g_symaddr, "Symbol Address" }, + { DISASM_OPT_MACRO, &g_macroon, "Macros" }, + { DISASM_OPT_PRINTREAL, &g_printreal, "Print Real Address" }, + { DISASM_OPT_PRINTREGS, &g_printregs, "Print Regs" }, + { DISASM_OPT_PRINTSWAP, &g_printswap, "Print Swap" }, + { DISASM_OPT_SIGNEDHEX, &g_signedhex, "Signed Hex" }, +}; + +SymbolType disasmResolveSymbol(unsigned int PC, char *name, int namelen) +{ + SymbolEntry *s; + SymbolType type = SYMBOL_NOSYM; + + if(g_syms) + { + s = (*g_syms)[PC]; + if(s) + { + type = s->type; + snprintf(name, namelen, "%s", s->name.c_str()); + } + } + + return type; +} + +SymbolEntry* disasmFindSymbol(unsigned int PC) +{ + SymbolEntry *s = NULL; + + if(g_syms) + { + s = (*g_syms)[PC]; + } + + return s; +} + +int disasmIsBranch(unsigned int opcode, unsigned int PC, unsigned int *dwTarget) +{ + int i; + int size; + int type = 0; + + size = sizeof(g_inst) / sizeof(Instruction); + for(i = 0; i < size; i++) + { + if(((opcode & g_inst[i].mask) == g_inst[i].opcode) && (g_inst[i].type & INSTR_TYPE_BRANCH)) + { + unsigned int addr; + int ofs; + + switch(g_inst[i].addrtype) + { + case ADDR_TYPE_16: ofs = (signed short) (opcode & 0xFFFF); + addr = PC + 4 + ofs * 4; + break; + case ADDR_TYPE_26: addr = (opcode & 0x03FFFFFF) << 2; + addr += PC & 0xF0000000; + break; + default: addr = 0xFFFFFFFF; + break; + }; + + if(addr == 0xFFFFFFFF) + { + break; + } + + if(dwTarget) + { + *dwTarget = addr; + } + type = g_inst[i].type; + } + } + + return type; +} + +void disasmAddBranchSymbols(unsigned int opcode, unsigned int PC, SymbolMap &syms) +{ + SymbolType type; + int insttype; + unsigned int addr; + SymbolEntry *s; + char buf[128]; + + insttype = disasmIsBranch(opcode, PC, &addr); + if(insttype != 0) + { + if(insttype & (INSTR_TYPE_B | INSTR_TYPE_JUMP)) + { + snprintf(buf, sizeof(buf), "loc_%08X", addr); + type = SYMBOL_LOCAL; + } + else + { + snprintf(buf, sizeof(buf), "sub_%08X", addr); + type = SYMBOL_FUNC; + } + + s = syms[addr]; + if(s == NULL) + { + s = new SymbolEntry; + s->addr = addr; + s->type = type; + s->size = 0; + s->name = buf; + s->refs.insert(s->refs.end(), PC); + syms[addr] = s; + } + else + { + if((s->type != SYMBOL_FUNC) && (type == SYMBOL_FUNC)) + { + s->type = SYMBOL_FUNC; + } + s->refs.insert(s->refs.end(), PC); + } + } +} + +void disasmSetHexInts(int hexints) +{ + g_hexints = hexints; +} + +void disasmSetMRegs(int mregs) +{ + g_mregs = mregs; +} + +void disasmSetSymAddr(int symaddr) +{ + g_symaddr = symaddr; +} + +void disasmSetMacro(int macro) +{ + g_macroon = macro; +} + +void disasmSetPrintReal(int printreal) +{ + g_printreal = printreal; +} + +void disasmSetSymbols(SymbolMap *syms) +{ + g_syms = syms; +} + +void disasmSetOpts(const char *opts) +{ + /* Initial mode is set */ + int set = 1; + + while(*opts) + { + char ch; + int i; + + ch = *opts++; + if(ch == '+') + { + set = 1; + } + else if(ch == '-') + { + set = 0; + } + else + { + for(i = 0; i < DISASM_OPT_MAX; i++) + { + if(ch == g_disopts[i].opt) + { + *g_disopts[i].value = set; + break; + } + } + if(i == DISASM_OPT_MAX) + { + printf("Unknown disassembler option '%c'\n", ch); + } + } + } +} + +void disasmPrintOpts(void) +{ + int i; + + printf("Disassembler Options:\n"); + for(i = 0; i < DISASM_OPT_MAX; i++) + { + printf("%c : %-3s - %s \n", g_disopts[i].opt, *g_disopts[i].value ? "on" : "off", + g_disopts[i].name); + } +} + +static char *print_cpureg(int reg, char *output) +{ + int len; + + if(!g_mregs) + { + len = sprintf(output, "$%s", regName[reg]); + } + else + { + if(reg > 0) + { + len = sprintf(output, "r%d", reg); + } + else + { + *output = '0'; + *(output+1) = 0; + len = 1; + } + } + + if(g_printregs) + { + g_regmask |= (1 << reg); + } + + return output + len; +} + +static char *print_int(int i, char *output) +{ + int len; + + len = sprintf(output, "%d", i); + + return output + len; +} + +static char *print_hex(int i, char *output) +{ + int len; + + len = sprintf(output, "0x%X", i); + + return output + len; +} + +static char *print_imm(int ofs, char *output) +{ + int len; + + if(g_hexints) + { + if((g_signedhex) && (ofs < 0)) + { + int real; + + real = -ofs; + len = sprintf(output, "-0x%X", real); + } + else + { + unsigned int val = ofs; + val &= 0xFFFF; + len = sprintf(output, "0x%X", val); + } + } + else + { + len = sprintf(output, "%d", ofs); + } + + return output + len; +} + +static char *print_jump(unsigned int addr, char *output) +{ + int len; + char symbol[128]; + int symfound = 0; + + if(g_syms) + { + symfound = disasmResolveSymbol(addr, symbol, sizeof(symbol)); + } + + if(symfound) + { + len = sprintf(output, "%s", symbol); + } + else + { + len = sprintf(output, "0x%08X", addr); + } + + return output + len; +} + +static char *print_ofs(int ofs, int reg, char *output, unsigned int *realregs) +{ + if((g_printreal) && (realregs)) + { + output = print_jump(realregs[reg] + ofs, output); + } + else + { + output = print_imm(ofs, output); + *output++ = '('; + + output = print_cpureg(reg, output); + *output++ = ')'; + } + + return output; +} + +static char *print_pcofs(int ofs, unsigned int PC, char *output) +{ + ofs = ofs * 4; + + return print_jump(PC + 4 + ofs, output); +} + +static char *print_jumpr(int reg, char *output, unsigned int *realregs) +{ + if((g_printreal) && (realregs)) + { + return print_jump(realregs[reg], output); + } + + return print_cpureg(reg, output); +} + +static char *print_syscall(unsigned int syscall, char *output) +{ + int len; + + len = sprintf(output, "0x%X", syscall); + + return output + len; +} + +static char *print_cop0(int reg, char *output) +{ + int len; + + if(cop0_regs[reg]) + { + len = sprintf(output, "%s", cop0_regs[reg]); + } + else + { + len = sprintf(output, "$%d", reg); + } + + return output + len; +} + +static char *print_cop1(int reg, char *output) +{ + int len; + + len = sprintf(output, "$fcr%d", reg); + + return output + len; +} + +// [hlide] added vfpu_extra_regs +static const char * const vfpu_extra_regs[] = +{ + "VFPU_PFXS", + "VFPU_PFXT", + "VFPU_PFXD", + "VFPU_CC ", + "VFPU_INF4", + NULL, + NULL, + "VFPU_REV", + "VFPU_RCX0", + "VFPU_RCX1", + "VFPU_RCX2", + "VFPU_RCX3", + "VFPU_RCX4", + "VFPU_RCX5", + "VFPU_RCX6", + "VFPU_RCX7" +}; + +// [hlide] added print_cop2 +static char *print_cop2(int reg, char *output) +{ + int len; + + if ((reg >= 128) && (reg < 128+16) && (vfpu_extra_regs[reg - 128])) + { + len = sprintf(output, "%s", vfpu_extra_regs[reg - 128]); + } + else + { + len = sprintf(output, "$%d", reg); + } + + return output + len; +} + +// [hlide] added vfpu_cond_names +static const char * const vfpu_cond_names[16] = +{ + "FL", "EQ", "LT", "LE", + "TR", "NE", "GE", "GT", + "EZ", "EN", "EI", "ES", + "NZ", "NN", "NI", "NS" +}; + +// [hlide] added print_vfpu_cond +static char *print_vfpu_cond(int cond, char *output) +{ + int len; + + if ((cond >= 0) && (cond < 16)) + { + len = sprintf(output, "%s", vfpu_cond_names[cond]); + } + else + { + len = sprintf(output, "%d", cond); + } + + return output + len; +} + +// [hlide] added vfpu_const_names +static const char * const vfpu_const_names[20] = +{ + "", + "VFPU_HUGE", + "VFPU_SQRT2", + "VFPU_SQRT1_2", + "VFPU_2_SQRTPI", + "VFPU_2_PI", + "VFPU_1_PI", + "VFPU_PI_4", + "VFPU_PI_2", + "VFPU_PI", + "VFPU_E", + "VFPU_LOG2E", + "VFPU_LOG10E", + "VFPU_LN2", + "VFPU_LN10", + "VFPU_2PI", + "VFPU_PI_6", + "VFPU_LOG10TWO", + "VFPU_LOG2TEN", + "VFPU_SQRT3_2" +}; + +// [hlide] added print_vfpu_const +static char *print_vfpu_const(int k, char *output) +{ + int len; + + if ((k > 0) && (k < 20)) + { + len = sprintf(output, "%s", vfpu_const_names[k]); + } + else + { + len = sprintf(output, "%d", k); + } + + return output + len; +} + +/* VFPU 16-bit floating-point format. */ +#define VFPU_FLOAT16_EXP_MAX 0x1f +#define VFPU_SH_FLOAT16_SIGN 15 +#define VFPU_MASK_FLOAT16_SIGN 0x1 +#define VFPU_SH_FLOAT16_EXP 10 +#define VFPU_MASK_FLOAT16_EXP 0x1f +#define VFPU_SH_FLOAT16_FRAC 0 +#define VFPU_MASK_FLOAT16_FRAC 0x3ff + +// [hlide] added print_vfpu_halffloat +static char *print_vfpu_halffloat(int l, char *output) +{ + int len; + + /* Convert a VFPU 16-bit floating-point number to IEEE754. */ + union float2int + { + unsigned int i; + float f; + } float2int; + unsigned short float16 = l & 0xFFFF; + unsigned int sign = (float16 >> VFPU_SH_FLOAT16_SIGN) & VFPU_MASK_FLOAT16_SIGN; + int exponent = (float16 >> VFPU_SH_FLOAT16_EXP) & VFPU_MASK_FLOAT16_EXP; + unsigned int fraction = float16 & VFPU_MASK_FLOAT16_FRAC; + char signchar = '+' + ((sign == 1) * 2); + + if (exponent == VFPU_FLOAT16_EXP_MAX) + { + if (fraction == 0) + len = sprintf(output, "%cInf", signchar); + else + len = sprintf(output, "%cNaN", signchar); + } + else if (exponent == 0 && fraction == 0) + { + len = sprintf(output, "%c0", signchar); + } + else + { + if (exponent == 0) + { + do + { + fraction <<= 1; + exponent--; + } + while (!(fraction & (VFPU_MASK_FLOAT16_FRAC + 1))); + + fraction &= VFPU_MASK_FLOAT16_FRAC; + } + + /* Convert to 32-bit single-precision IEEE754. */ + float2int.i = sign << 31; + float2int.i |= (exponent + 112) << 23; + float2int.i |= fraction << 13; + len = sprintf(output, "%g", float2int.f); + } + + return output + len; +} + +// [hlide] added pfx_cst_names +static const char * const pfx_cst_names[8] = +{ + "0", "1", "2", "1/2", "3", "1/3", "1/4", "1/6" +}; + +// [hlide] added pfx_swz_names +static const char * const pfx_swz_names[4] = +{ + "x", "y", "z", "w" +}; + +// [hlide] added pfx_sat_names +static const char * const pfx_sat_names[4] = +{ + "", "[0:1]", "", "[-1:1]" +}; + +/* VFPU prefix instruction operands. The *_SH_* values really specify where + the bitfield begins, as VFPU prefix instructions have four operands + encoded within the immediate field. */ +#define VFPU_SH_PFX_NEG 16 +#define VFPU_MASK_PFX_NEG 0x1 /* Negation. */ +#define VFPU_SH_PFX_CST 12 +#define VFPU_MASK_PFX_CST 0x1 /* Constant. */ +#define VFPU_SH_PFX_ABS_CSTHI 8 +#define VFPU_MASK_PFX_ABS_CSTHI 0x1 /* Abs/Constant (bit 2). */ +#define VFPU_SH_PFX_SWZ_CSTLO 0 +#define VFPU_MASK_PFX_SWZ_CSTLO 0x3 /* Swizzle/Constant (bits 0-1). */ +#define VFPU_SH_PFX_MASK 8 +#define VFPU_MASK_PFX_MASK 0x1 /* Mask. */ +#define VFPU_SH_PFX_SAT 0 +#define VFPU_MASK_PFX_SAT 0x3 /* Saturation. */ + +// [hlide] added print_vfpu_prefix +static char *print_vfpu_prefix(int l, unsigned int pos, char *output) +{ + int len = 0; + + switch (pos) + { + case '0': + case '1': + case '2': + case '3': + { + unsigned int base = '0'; + unsigned int negation = (l >> (pos - (base - VFPU_SH_PFX_NEG))) & VFPU_MASK_PFX_NEG; + unsigned int constant = (l >> (pos - (base - VFPU_SH_PFX_CST))) & VFPU_MASK_PFX_CST; + unsigned int abs_consthi = (l >> (pos - (base - VFPU_SH_PFX_ABS_CSTHI))) & VFPU_MASK_PFX_ABS_CSTHI; + unsigned int swz_constlo = (l >> ((pos - base) * 2)) & VFPU_MASK_PFX_SWZ_CSTLO; + + if (negation) + len = sprintf(output, "-"); + if (constant) + { + len += sprintf(output+len, "%s", pfx_cst_names[(abs_consthi << 2) | swz_constlo]); + } + else + { + if (abs_consthi) + len += sprintf(output+len, "|%s|", pfx_swz_names[swz_constlo]); + else + len += sprintf(output+len, "%s", pfx_swz_names[swz_constlo]); + } + } + break; + + case '4': + case '5': + case '6': + case '7': + { + unsigned int base = '4'; + unsigned int mask = (l >> (pos - (base - VFPU_SH_PFX_MASK))) & VFPU_MASK_PFX_MASK; + unsigned int saturation = (l >> ((pos - base) * 2)) & VFPU_MASK_PFX_SAT; + + if (mask) + len += sprintf(output, "m"); + else + len += sprintf(output, "%s", pfx_sat_names[saturation]); + } + break; + } + + return output + len; +} + +/* Special handling of the vrot instructions. */ +#define VFPU_MASK_OP_SIZE 0x8080 /* Masks the operand size (pair, triple, quad). */ +#define VFPU_OP_SIZE_PAIR 0x80 +#define VFPU_OP_SIZE_TRIPLE 0x8000 +#define VFPU_OP_SIZE_QUAD 0x8080 +/* Note that these are within the rotators field, and not the full opcode. */ +#define VFPU_SH_ROT_HI 2 +#define VFPU_MASK_ROT_HI 0x3 +#define VFPU_SH_ROT_LO 0 +#define VFPU_MASK_ROT_LO 0x3 +#define VFPU_SH_ROT_NEG 4 /* Negation. */ +#define VFPU_MASK_ROT_NEG 0x1 + +// [hlide] added print_vfpu_rotator +static char *print_vfpu_rotator(int l, char *output) +{ + int len; + + const char *elements[4]; + + unsigned int opcode = l & VFPU_MASK_OP_SIZE; + unsigned int rotators = (l >> 16) & 0x1f; + unsigned int opsize, rothi, rotlo, negation, i; + + /* Determine the operand size so we'll know how many elements to output. */ + if (opcode == VFPU_OP_SIZE_PAIR) + opsize = 2; + else if (opcode == VFPU_OP_SIZE_TRIPLE) + opsize = 3; + else + opsize = (opcode == VFPU_OP_SIZE_QUAD) * 4; /* Sanity check. */ + + rothi = (rotators >> VFPU_SH_ROT_HI) & VFPU_MASK_ROT_HI; + rotlo = (rotators >> VFPU_SH_ROT_LO) & VFPU_MASK_ROT_LO; + negation = (rotators >> VFPU_SH_ROT_NEG) & VFPU_MASK_ROT_NEG; + + if (rothi == rotlo) + { + if (negation) + { + elements[0] = "-s"; + elements[1] = "-s"; + elements[2] = "-s"; + elements[3] = "-s"; + } + else + { + elements[0] = "s"; + elements[1] = "s"; + elements[2] = "s"; + elements[3] = "s"; + } + } + else + { + elements[0] = "0"; + elements[1] = "0"; + elements[2] = "0"; + elements[3] = "0"; + } + if (negation) + elements[rothi] = "-s"; + else + elements[rothi] = "s"; + elements[rotlo] = "c"; + + len = sprintf(output, "["); + + for (i = 0;;) + { + len += sprintf(output, "%s", elements[i++]); + if (i >= opsize) + break; + sprintf(output, " ,"); + } + + len += sprintf(output, "]"); + + return output + len; +} + +static char *print_fpureg(int reg, char *output) +{ + int len; + + len = sprintf(output, "$fpr%02d", reg); + + return output + len; +} + +static char *print_debugreg(int reg, char *output) +{ + int len; + + if((reg < 16) && (dr_regs[reg])) + { + len = sprintf(output, "%s", dr_regs[reg]); + } + else + { + len = sprintf(output, "$%02d\n", reg); + } + + return output + len; +} + +static char *print_vfpusingle(int reg, char *output) +{ + int len; + + len = sprintf(output, "S%d%d%d", (reg >> 2) & 7, reg & 3, (reg >> 5) & 3); + + return output + len; +} + +static char *print_vfpu_reg(int reg, int offset, char one, char two, char *output) +{ + int len; + + if((reg >> 5) & 1) + { + len = sprintf(output, "%c%d%d%d", two, (reg >> 2) & 7, offset, reg & 3); + } + else + { + len = sprintf(output, "%c%d%d%d", one, (reg >> 2) & 7, reg & 3, offset); + } + + return output + len; +} + +static char *print_vfpuquad(int reg, char *output) +{ + return print_vfpu_reg(reg, 0, 'C', 'R', output); +} + +static char *print_vfpupair(int reg, char *output) +{ + if((reg >> 6) & 1) + { + return print_vfpu_reg(reg, 2, 'C', 'R', output); + } + else + { + return print_vfpu_reg(reg, 0, 'C', 'R', output); + } +} + +static char *print_vfputriple(int reg, char *output) +{ + if((reg >> 6) & 1) + { + return print_vfpu_reg(reg, 1, 'C', 'R', output); + } + else + { + return print_vfpu_reg(reg, 0, 'C', 'R', output); + } +} + +static char *print_vfpumpair(int reg, char *output) +{ + if((reg >> 6) & 1) + { + return print_vfpu_reg(reg, 2, 'M', 'E', output); + } + else + { + return print_vfpu_reg(reg, 0, 'M', 'E', output); + } +} + +static char *print_vfpumtriple(int reg, char *output) +{ + if((reg >> 6) & 1) + { + return print_vfpu_reg(reg, 1, 'M', 'E', output); + } + else + { + return print_vfpu_reg(reg, 0, 'M', 'E', output); + } +} + +static char *print_vfpumatrix(int reg, char *output) +{ + return print_vfpu_reg(reg, 0, 'M', 'E', output); +} + +static char *print_vfpureg(int reg, char type, char *output) +{ + switch(type) + { + case 's': return print_vfpusingle(reg, output); + break; + case 'q': return print_vfpuquad(reg, output); + break; + case 'p': return print_vfpupair(reg, output); + break; + case 't': return print_vfputriple(reg, output); + break; + case 'm': return print_vfpumpair(reg, output); + break; + case 'n': return print_vfpumtriple(reg, output); + break; + case 'o': return print_vfpumatrix(reg, output); + break; + default: break; + }; + + return output; +} + +static void decode_args(unsigned int opcode, unsigned int PC, const char *fmt, char *output, unsigned int *realregs) +{ + int i = 0; + int vmmul = 0; + + while(fmt[i]) + { + if(fmt[i] == '%') + { + i++; + switch(fmt[i]) + { + case 'd': output = print_cpureg(RD(opcode), output); + break; + case 't': output = print_cpureg(RT(opcode), output); + break; + case 's': output = print_cpureg(RS(opcode), output); + break; + case 'i': output = print_imm(IMM(opcode), output); + break; + case 'I': output = print_hex(IMMU(opcode), output); + break; + case 'o': output = print_ofs(IMM(opcode), RS(opcode), output, realregs); + break; + case 'O': output = print_pcofs(IMM(opcode), PC, output); + break; + case 'V': output = print_ofs(IMM(opcode), RS(opcode), output, realregs); + break; + case 'j': output = print_jump(JUMP(opcode, PC), output); + break; + case 'J': output = print_jumpr(RS(opcode), output, realregs); + break; + case 'a': output = print_int(SA(opcode), output); + break; + case '0': output = print_cop0(RD(opcode), output); + break; + case '1': output = print_cop1(RD(opcode), output); + break; + case 'p': *output++ = '$'; + output = print_int(RD(opcode), output); + break; + case '2': // [hlide] added %2? (? is d, s) + switch (fmt[i+1]) { + case 'd' : output = print_cop2(VED(opcode), output); i++; break; + case 's' : output = print_cop2(VES(opcode), output); i++; break; + } + break; + case 'k': output = print_hex(RT(opcode), output); + break; + case 'D': output = print_fpureg(FD(opcode), output); + break; + case 'T': output = print_fpureg(FT(opcode), output); + break; + case 'S': output = print_fpureg(FS(opcode), output); + break; + case 'r': output = print_debugreg(RD(opcode), output); + break; + case 'n': // [hlide] completed %n? (? is e, i) + switch (fmt[i+1]) { + case 'e' : output = print_int(RD(opcode) + 1, output); i++; break; + case 'i' : output = print_int(RD(opcode) - SA(opcode) + 1, output); i++; break; + } + break; + case 'x': if(fmt[i+1]) { output = print_vfpureg(VT(opcode), fmt[i+1], output); i++; }break; + case 'y': if(fmt[i+1]) { + int reg = VS(opcode); + if(vmmul) { if(reg & 0x20) { reg &= 0x5F; } else { reg |= 0x20; } } + output = print_vfpureg(reg, fmt[i+1], output); i++; + } + break; + case 'z': if(fmt[i+1]) { output = print_vfpureg(VD(opcode), fmt[i+1], output); i++; } + break; + case 'v': // [hlide] completed %v? (? is 3, 5, 8, k, i, h, r, p? (? is (0, 1, 2, 3, 4, 5, 6, 7) ) ) + switch (fmt[i+1]) { + case '3' : output = print_int(VI3(opcode), output); i++; break; + case '5' : output = print_int(VI5(opcode), output); i++; break; + case '8' : output = print_int(VI8(opcode), output); i++; break; + case 'k' : output = print_vfpu_const(VI5(opcode), output); i++; break; + case 'i' : output = print_int(IMM(opcode), output); i++; break; + case 'h' : output = print_vfpu_halffloat(opcode, output); i++; break; + case 'r' : output = print_vfpu_rotator(opcode, output); i++; break; + case 'p' : if (fmt[i+2]) { output = print_vfpu_prefix(opcode, fmt[i+2], output); i += 2; } + break; + } + break; + case 'X': if(fmt[i+1]) { output = print_vfpureg(VO(opcode), fmt[i+1], output); i++; } + break; + case 'Z': // [hlide] modified %Z to %Z? (? is c, n) + switch (fmt[i+1]) { + case 'c' : output = print_imm(VCC(opcode), output); i++; break; + case 'n' : output = print_vfpu_cond(VCN(opcode), output); i++; break; + } + break; + case 'c': output = print_hex(CODE(opcode), output); + break; + case 'C': output = print_syscall(CODE(opcode), output); + break; + case 'Y': output = print_ofs(IMM(opcode) & ~3, RS(opcode), output, realregs); + break; + case '?': vmmul = 1; + break; + case 0: goto end; + default: break; + }; + i++; + } + else + { + *output++ = fmt[i++]; + } + } +end: + + *output = 0; +} + +void format_line(char *code, int codelen, const char *addr, unsigned int opcode, const char *name, const char *args, int noaddr) +{ + char ascii[17]; + char *p; + int i; + + if(name == NULL) + { + name = "Unknown"; + args = ""; + } + + p = ascii; + for(i = 0; i < 4; i++) + { + unsigned char ch; + + ch = (unsigned char) ((opcode >> (i*8)) & 0xFF); + if((ch < 32) || (ch > 126)) + { + ch = '.'; + } + + *p++ = ch; + } + *p = 0; + + if(noaddr) + { + snprintf(code, codelen, "%-10s %s", name, args); + } + else + { + if(g_printswap) + { + snprintf(code, codelen, "%-10s %-30s ; %s: 0x%08X '%s'", name, args, addr, opcode, ascii); + } + else + { + snprintf(code, codelen, "%s: 0x%08X '%s' - %-10s %s", addr, opcode, ascii, name, args); + } + } +} + +const char *disasmInstruction(unsigned int opcode, unsigned int PC, unsigned int *realregs, unsigned int *regmask, int noaddr) +{ + static char code[1024]; + const char *name = NULL; + char args[1024]; + char addr[1024]; + int size; + int i; + struct Instruction *ix = NULL; + + sprintf(addr, "0x%08X", PC); + if((g_syms) && (g_symaddr)) + { + char addrtemp[128]; + /* Symbol resolver shouldn't touch addr unless it finds symbol */ + if(disasmResolveSymbol(PC, addrtemp, sizeof(addrtemp))) + { + snprintf(addr, sizeof(addr), "%-20s", addrtemp); + } + } + + g_regmask = 0; + + if(!g_macroon) + { + size = sizeof(g_macro) / sizeof(struct Instruction); + for(i = 0; i < size; i++) + { + if((opcode & g_macro[i].mask) == g_macro[i].opcode) + { + ix = &g_macro[i]; + break; + } + } + } + + if(!ix) + { + size = sizeof(g_inst) / sizeof(struct Instruction); + for(i = 0; i < size; i++) + { + if((opcode & g_inst[i].mask) == g_inst[i].opcode) + { + ix = &g_inst[i]; + break; + } + } + } + + if(ix) + { + decode_args(opcode, PC, ix->fmt, args, realregs); + + if(regmask) + { + *regmask = g_regmask; + } + + name = ix->name; + } + + format_line(code, sizeof(code), addr, opcode, name, args, noaddr); + + return code; +} diff --git a/pspsh/disasm.h b/pspsh/disasm.h new file mode 100644 index 0000000..33072e6 --- /dev/null +++ b/pspsh/disasm.h @@ -0,0 +1,83 @@ +/*************************************************************** + * PRXTool : Utility for PSP executables. + * (c) TyRaNiD 2k6 + * + * disasm.h - Implementation of a MIPS disassembler + ***************************************************************/ +#ifndef __DISASM_H__ +#define __DISASM_H__ + +#include +#include +#include + +enum SymbolType +{ + SYMBOL_NOSYM = 0, + SYMBOL_UNK, + SYMBOL_FUNC, + SYMBOL_LOCAL, + SYMBOL_DATA, +}; + +typedef std::vector RefMap; +typedef std::vector AliasMap; + +struct SymbolEntry +{ + unsigned int addr; + SymbolType type; + unsigned int size; + std::string name; + RefMap refs; + AliasMap alias; +}; + +typedef std::map SymbolMap; + +struct ImmEntry +{ + unsigned int addr; + unsigned int target; + /* Does this entry point to a text section ? */ + int text; +}; + +typedef std::map ImmMap; + +#define DISASM_OPT_MAX 8 +#define DISASM_OPT_HEXINTS 'x' +#define DISASM_OPT_MREGS 'r' +#define DISASM_OPT_SYMADDR 's' +#define DISASM_OPT_MACRO 'm' +#define DISASM_OPT_PRINTREAL 'p' +#define DISASM_OPT_PRINTREGS 'g' +#define DISASM_OPT_PRINTSWAP 'w' +#define DISASM_OPT_SIGNEDHEX 'd' + +#define INSTR_TYPE_PSP 1 +#define INSTR_TYPE_B 2 +#define INSTR_TYPE_JUMP 4 +#define INSTR_TYPE_JAL 8 + +/* Enable hexadecimal integers for immediates */ +void disasmSetHexInts(int hexints); +/* Enable mnemonic MIPS registers */ +void disasmSetMRegs(int mregs); +/* Enable resolving of PC to a symbol if available */ +void disasmSetSymAddr(int symaddr); +/* Enable instruction macros */ +void disasmSetMacro(int macro); +void disasmSetPrintReal(int printreal); +void disasmSetOpts(const char *opts); +const char *disasmGetOpts(void); +void disasmPrintOpts(void); +const char *disasmInstruction(unsigned int opcode, unsigned int PC, unsigned int *realregs, unsigned int *regmask, int noaddr); + +void disasmSetSymbols(SymbolMap *syms); +void disasmAddBranchSymbols(unsigned int opcode, unsigned int PC, SymbolMap &syms); +SymbolType disasmResolveSymbol(unsigned int PC, char *name, int namelen); +SymbolEntry* disasmFindSymbol(unsigned int PC); +int disasmIsBranch(unsigned int opcode, unsigned int PC, unsigned int *dwTarget); + +#endif diff --git a/pspsh/disasm.o b/pspsh/disasm.o new file mode 100644 index 0000000..aae793d Binary files /dev/null and b/pspsh/disasm.o differ diff --git a/pspsh/parse_args.C b/pspsh/parse_args.C new file mode 100644 index 0000000..53d38cd --- /dev/null +++ b/pspsh/parse_args.C @@ -0,0 +1,448 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * parse_args.c - PSPLINK argument parser code + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/pspsh/parse_args.C $ + * $Id: parse_args.C 2222 2007-04-23 19:12:30Z tyranid $ + */ + +#include +#include +#include +#include +#include +#include +#include "parse_args.h" + +int hex_to_int(char ch) +{ + ch = toupper(ch); + if((ch >= '0') && (ch <= '9')) + { + return ch-'0'; + } + else if((ch >= 'A') && (ch <= 'F')) + { + return ch-'A'; + } + + return 0; +} + +int oct_to_int(char ch) +{ + if((ch >= '0') && (ch < '8')) + { + return ch-'0'; + } + + return 0; +} + +int isodigit(char ch) +{ + if((ch >= '0') && (ch < '8')) + { + return 1; + } + + return 0; +} + +int decode_hex(const char *str, unsigned char *ch) +{ + int i; + + *ch = 0; + for(i = 0; i < 2; i++) + { + if(!isxdigit(str[i])) + { + break; + } + *ch = *ch << 4; + *ch = *ch | hex_to_int(str[i]); + } + if(i == 0) + { + printf("Missing following hex characters\n"); + return 0; + } + if(*ch == 0) + { + printf("Invalid hex character (not allowed NULs)\n"); + return 0; + } + + return i; +} + +int decode_oct(const char *str, unsigned char *ch) +{ + int i; + + *ch = 0; + for(i = 0; i < 4; i++) + { + if(!isodigit(str[i])) + { + break; + } + *ch = *ch << 3; + *ch = *ch | oct_to_int(str[i]); + } + if(i == 0) + { + printf("Missing following octal characters\n"); + return 0; + } + if(*ch == 0) + { + printf("Invalid octal character (not allowed NULs)\n"); + return 0; + } + + return i; +} + +/* Insert an arg or environment variable */ +void insert_arg(const char **pin, char **pout, int sargc, char**sargv) +{ + const char *in = *pin; + char *out = *pout; + + /* If an argument */ + if(isdigit(*in)) + { + char *endp; + int val = strtoul(in, &endp, 10); + in = endp; + if((val < sargc) && (sargv) && (sargv[val])) + { + int len = strlen(sargv[val]); + memcpy(out, sargv[val], len); + out += len; + } + } + else + { + char name[PATH_MAX]; + char *pname = name; + + if(*in == '(') /* Alpha numeric variables $(name) */ + { + in++; + while((*in != 0) && (*in != ')')) + { + *pname++ = *in++; + } + + if(*in != ')') + { + /* Error, escape with an empty string */ + fprintf(stderr, "Warning: No matching ')' for variable\n"); + name[0] = 0; + } + else + { + in++; + } + + *pname = 0; + } + /* Punctuation, internal variables */ + else if(ispunct(*in)) + { + name[0] = *in++; + name[1] = 0; + } + else + { + /* Just restore the dollar sign */ + name[0] = 0; + *out++ = '$'; + } + if(name[0]) + { + char *val = getenv(name); + int len; + if(val) + { + len = strlen(val); + memcpy(out, val, len); + out += len; + } + } + } + + *pout = out; + *pin = in; +} + +/* Read a single string from the output, escape characters and quotes, insert $ args */ +int read_string(const char **pin, char **pout, int sargc, char **sargv) +{ + int in_quote = 0; + const char *in = *pin; + char *out = *pout; + int len = 0; + int error = 0; + + while(isspace(*in)) + { + in++; + } + + if(*in == 0) + { + *pin = in; + return 0; + } + + while((*in != 0) && (error == 0)) + { + /* Escape character */ + if(*in == '\\') + { + if(in_quote == '\'') + { + *out++ = *in++; + if(*in) + { + *out++ = *in++; + } + } + else + { + /* Skip the escape */ + in++; + switch(*in) + { + case 'n': *out++ = 10; + in++; + break; + case 'r': *out++ = 13; + in++; + break; + case '0': /* Octal */ + { + int i; + unsigned char ch; + in++; + i = decode_oct(in, &ch); + if((i == 0) || (i == 1)) + { + error = 1; + break; + } + in += i; + *out++ = ch; + } + break; + case 'x': /* Hexadecimal */ + { + int i; + unsigned char ch; + in++; + i = decode_hex(in, &ch); + if((i == 0) || (i == 1)) + { + error = 1; + break; + } + in += i; + *out++ = ch; + } + break; + case 0 : break; /* End of string */ + default : *out++ = *in++; + break; + }; + } + } + else + { + if((isspace(*in)) && (in_quote == 0)) + { + while(isspace(*in)) + { + in++; + } + break; + } + else if((*in == '>') && (in_quote == 0)) + { + break; + } + else if((*in == '"') || (*in == '\'')) + { + if(in_quote) + { + if(*in == in_quote) + { + in_quote = 0; + in++; + } + else + { + *out++ = *in++; + } + } + else + { + in_quote = *in; + in++; + } + } + else + { + if((*in == '$') && (in_quote != '\'')) + { + in++; + insert_arg(&in, &out, sargc, sargv); + } + else + { + *out++ = *in++; + } + } + } + } + + if(in_quote) + { + printf("Missing matching quote %c\n", in_quote); + } + else if(error) + { + printf("Error in command line\n"); + } + else + { + *out++ = 0; + len = out - *pout; + } + + *pin = in; + *pout = out; + return len; +} + +int parse_cli(const char *in, char *out, int *argc, char **argv, int max_args, int sargc, char **sargv, int *type, char *redir) +{ + char *lastout; + char *outstart = out; + + if((in == NULL) || (out == NULL) || (argc == NULL) || (argv == NULL) || (max_args <= 0) || (type == NULL) || (redir == NULL)) + { + printf("Error in parse_args, invalid arguments\n"); + return 0; + } + + *argc = 0; + *type = REDIR_TYPE_NONE; + + /* Skip any leading white space */ + while(isspace(*in)) + { + in++; + } + + if(*in == 0) + { + return 0; + } + + lastout = out; + while(*argc < (max_args-1)) + { + /* Parse shell characters */ + if(*in == '>') + { + char *outfile = redir; + + in++; + if(*in == '>') + { + *type = REDIR_TYPE_CAT; + in++; + } + else + { + *type = REDIR_TYPE_NEW; + } + + if(read_string(&in, &outfile, sargc, sargv) == 0) + { + printf("Error in redirection, no filename\n"); + return 0; + } + } + else + { + if(read_string(&in, &out, sargc, sargv) == 0) + { + break; + } + + argv[*argc] = lastout; + *argc += 1; + lastout = out; + } + } + + argv[*argc] = NULL; + /* A command ends with a 1 */ + *out++ = 1; + + return out-outstart; +} + +#ifdef _TEST +int main(void) +{ + char str[1024]; + char out[1024]; + + while(fgets(str, sizeof(str), stdin)) + { + char *argv[16]; + char redir[1025]; + int type = 0; + int argc; + int binlen; + + binlen = parse_cli("test me\n", out, &argc, argv, 16, 0, NULL, &type, redir); + if(binlen > 0) + { + int i; + for(i = 0; i < argc; i++) + { + printf("Arg %d: '%s'\n", i, argv[i]); + } + + for(i = 0; i < binlen; i++) + { + if(out[i] < 32) + { + printf("\\x%02X", out[i]); + } + else + { + printf("%c", out[i]); + } + } + printf("\n"); + if(type > 0) + { + printf("Redir type %d, '%s'\n", type, redir); + } + } + } +} +#endif diff --git a/pspsh/parse_args.h b/pspsh/parse_args.h new file mode 100644 index 0000000..a3ba075 --- /dev/null +++ b/pspsh/parse_args.h @@ -0,0 +1,19 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * parse_args.h - PSPLINK argument parser code + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/psplink/parse_args.h $ + * $Id: parse_args.h 1604 2005-12-18 13:55:03Z tyranid $ + */ + +#define REDIR_TYPE_NONE 0 +#define REDIR_TYPE_NEW 1 +#define REDIR_TYPE_CAT 2 + +int parse_cli(const char *in, char *out, int *argc, char **argv, int max_args, int sargc, char **sargv, int *type, char *filename); +const char *parse_redir(const char *in, char *filename, int *type); diff --git a/pspsh/parse_args.o b/pspsh/parse_args.o new file mode 100644 index 0000000..9eb194f Binary files /dev/null and b/pspsh/parse_args.o differ diff --git a/pspsh/pspkerror.C b/pspsh/pspkerror.C new file mode 100644 index 0000000..9eafcd2 --- /dev/null +++ b/pspsh/pspkerror.C @@ -0,0 +1,215 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * pspkerror.C - Definitions for error codes + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/pspsh/pspkerror.C $ + * $Id: pspkerror.C 2200 2007-03-08 21:21:20Z tyranid $ + */ +#include "pspkerror.h" + +struct PspErrorCode PspKernelErrorCodes[] = +{ + { "SCE_KERNEL_ERROR_OK" , 0 }, + { "SCE_KERNEL_ERROR_ERROR" , 0x80020001 }, + { "SCE_KERNEL_ERROR_NOTIMP" , 0x80020002 }, + { "SCE_KERNEL_ERROR_ILLEGAL_EXPCODE" , 0x80020032 }, + { "SCE_KERNEL_ERROR_EXPHANDLER_NOUSE" , 0x80020033 }, + { "SCE_KERNEL_ERROR_EXPHANDLER_USED" , 0x80020034 }, + { "SCE_KERNEL_ERROR_SYCALLTABLE_NOUSED" , 0x80020035 }, + { "SCE_KERNEL_ERROR_SYCALLTABLE_USED" , 0x80020036 }, + { "SCE_KERNEL_ERROR_ILLEGAL_SYSCALLTABLE" , 0x80020037 }, + { "SCE_KERNEL_ERROR_ILLEGAL_PRIMARY_SYSCALL_NUMBER" , 0x80020038 }, + { "SCE_KERNEL_ERROR_PRIMARY_SYSCALL_NUMBER_INUSE" , 0x80020039 }, + { "SCE_KERNEL_ERROR_ILLEGAL_CONTEXT" , 0x80020064 }, + { "SCE_KERNEL_ERROR_ILLEGAL_INTRCODE" , 0x80020065 }, + { "SCE_KERNEL_ERROR_CPUDI" , 0x80020066 }, + { "SCE_KERNEL_ERROR_FOUND_HANDLER" , 0x80020067 }, + { "SCE_KERNEL_ERROR_NOTFOUND_HANDLER" , 0x80020068 }, + { "SCE_KERNEL_ERROR_ILLEGAL_INTRLEVEL" , 0x80020069 }, + { "SCE_KERNEL_ERROR_ILLEGAL_ADDRESS" , 0x8002006a }, + { "SCE_KERNEL_ERROR_ILLEGAL_INTRPARAM" , 0x8002006b }, + { "SCE_KERNEL_ERROR_ILLEGAL_STACK_ADDRESS" , 0x8002006c }, + { "SCE_KERNEL_ERROR_ALREADY_STACK_SET" , 0x8002006d }, + { "SCE_KERNEL_ERROR_NO_TIMER" , 0x80020096 }, + { "SCE_KERNEL_ERROR_ILLEGAL_TIMERID" , 0x80020097 }, + { "SCE_KERNEL_ERROR_ILLEGAL_SOURCE" , 0x80020098 }, + { "SCE_KERNEL_ERROR_ILLEGAL_PRESCALE" , 0x80020099 }, + { "SCE_KERNEL_ERROR_TIMER_BUSY" , 0x8002009a }, + { "SCE_KERNEL_ERROR_TIMER_NOT_SETUP" , 0x8002009b }, + { "SCE_KERNEL_ERROR_TIMER_NOT_INUSE" , 0x8002009c }, + { "SCE_KERNEL_ERROR_UNIT_USED" , 0x800200a0 }, + { "SCE_KERNEL_ERROR_UNIT_NOUSE" , 0x800200a1 }, + { "SCE_KERNEL_ERROR_NO_ROMDIR" , 0x800200a2 }, + { "SCE_KERNEL_ERROR_IDTYPE_EXIST" , 0x800200c8 }, + { "SCE_KERNEL_ERROR_IDTYPE_NOT_EXIST" , 0x800200c9 }, + { "SCE_KERNEL_ERROR_IDTYPE_NOT_EMPTY" , 0x800200ca }, + { "SCE_KERNEL_ERROR_UNKNOWN_UID" , 0x800200cb }, + { "SCE_KERNEL_ERROR_UNMATCH_UID_TYPE" , 0x800200cc }, + { "SCE_KERNEL_ERROR_ID_NOT_EXIST" , 0x800200cd }, + { "SCE_KERNEL_ERROR_NOT_FOUND_UIDFUNC" , 0x800200ce }, + { "SCE_KERNEL_ERROR_UID_ALREADY_HOLDER" , 0x800200cf }, + { "SCE_KERNEL_ERROR_UID_NOT_HOLDER" , 0x800200d0 }, + { "SCE_KERNEL_ERROR_ILLEGAL_PERM" , 0x800200d1 }, + { "SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT" , 0x800200d2 }, + { "SCE_KERNEL_ERROR_ILLEGAL_ADDR" , 0x800200d3 }, + { "SCE_KERNEL_ERROR_OUT_OF_RANGE" , 0x800200d4 }, + { "SCE_KERNEL_ERROR_MEM_RANGE_OVERLAP" , 0x800200d5 }, + { "SCE_KERNEL_ERROR_ILLEGAL_PARTITION" , 0x800200d6 }, + { "SCE_KERNEL_ERROR_PARTITION_INUSE" , 0x800200d7 }, + { "SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCKTYPE" , 0x800200d8 }, + { "SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED" , 0x800200d9 }, + { "SCE_KERNEL_ERROR_MEMBLOCK_RESIZE_LOCKED" , 0x800200da }, + { "SCE_KERNEL_ERROR_MEMBLOCK_RESIZE_FAILED" , 0x800200db }, + { "SCE_KERNEL_ERROR_HEAPBLOCK_ALLOC_FAILED" , 0x800200dc }, + { "SCE_KERNEL_ERROR_HEAP_ALLOC_FAILED" , 0x800200dd }, + { "SCE_KERNEL_ERROR_ILLEGAL_CHUNK_ID" , 0x800200de }, + { "SCE_KERNEL_ERROR_NOCHUNK" , 0x800200df }, + { "SCE_KERNEL_ERROR_NO_FREECHUNK" , 0x800200e0 }, + { "SCE_KERNEL_ERROR_LINKERR" , 0x8002012c }, + { "SCE_KERNEL_ERROR_ILLEGAL_OBJECT" , 0x8002012d }, + { "SCE_KERNEL_ERROR_UNKNOWN_MODULE" , 0x8002012e }, + { "SCE_KERNEL_ERROR_NOFILE" , 0x8002012f }, + { "SCE_KERNEL_ERROR_FILEERR" , 0x80020130 }, + { "SCE_KERNEL_ERROR_MEMINUSE" , 0x80020131 }, + { "SCE_KERNEL_ERROR_PARTITION_MISMATCH" , 0x80020132 }, + { "SCE_KERNEL_ERROR_ALREADY_STARTED" , 0x80020133 }, + { "SCE_KERNEL_ERROR_NOT_STARTED" , 0x80020134 }, + { "SCE_KERNEL_ERROR_ALREADY_STOPPED" , 0x80020135 }, + { "SCE_KERNEL_ERROR_CAN_NOT_STOP" , 0x80020136 }, + { "SCE_KERNEL_ERROR_NOT_STOPPED" , 0x80020137 }, + { "SCE_KERNEL_ERROR_NOT_REMOVABLE" , 0x80020138 }, + { "SCE_KERNEL_ERROR_EXCLUSIVE_LOAD" , 0x80020139 }, + { "SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED" , 0x8002013a }, + { "SCE_KERNEL_ERROR_LIBRARY_FOUND" , 0x8002013b }, + { "SCE_KERNEL_ERROR_LIBRARY_NOTFOUND" , 0x8002013c }, + { "SCE_KERNEL_ERROR_ILLEGAL_LIBRARY" , 0x8002013d }, + { "SCE_KERNEL_ERROR_LIBRARY_INUSE" , 0x8002013e }, + { "SCE_KERNEL_ERROR_ALREADY_STOPPING" , 0x8002013f }, + { "SCE_KERNEL_ERROR_ILLEGAL_OFFSET" , 0x80020140 }, + { "SCE_KERNEL_ERROR_ILLEGAL_POSITION" , 0x80020141 }, + { "SCE_KERNEL_ERROR_ILLEGAL_ACCESS" , 0x80020142 }, + { "SCE_KERNEL_ERROR_MODULE_MGR_BUSY" , 0x80020143 }, + { "SCE_KERNEL_ERROR_ILLEGAL_FLAG" , 0x80020144 }, + { "SCE_KERNEL_ERROR_CANNOT_GET_MODULELIST" , 0x80020145 }, + { "SCE_KERNEL_ERROR_PROHIBIT_LOADMODULE_DEVICE" , 0x80020146 }, + { "SCE_KERNEL_ERROR_PROHIBIT_LOADEXEC_DEVICE" , 0x80020147 }, + { "SCE_KERNEL_ERROR_UNSUPPORTED_PRX_TYPE" , 0x80020148 }, + { "SCE_KERNEL_ERROR_ILLEGAL_PERM_CALL" , 0x80020149 }, + { "SCE_KERNEL_ERROR_CANNOT_GET_MODULE_INFORMATION" , 0x8002014a }, + { "SCE_KERNEL_ERROR_ILLEGAL_LOADEXEC_BUFFER" , 0x8002014b }, + { "SCE_KERNEL_ERROR_ILLEGAL_LOADEXEC_FILENAME" , 0x8002014c }, + { "SCE_KERNEL_ERROR_NO_EXIT_CALLBACK" , 0x8002014d }, + { "SCE_KERNEL_ERROR_NO_MEMORY" , 0x80020190 }, + { "SCE_KERNEL_ERROR_ILLEGAL_ATTR" , 0x80020191 }, + { "SCE_KERNEL_ERROR_ILLEGAL_ENTRY" , 0x80020192 }, + { "SCE_KERNEL_ERROR_ILLEGAL_PRIORITY" , 0x80020193 }, + { "SCE_KERNEL_ERROR_ILLEGAL_STACK_SIZE" , 0x80020194 }, + { "SCE_KERNEL_ERROR_ILLEGAL_MODE" , 0x80020195 }, + { "SCE_KERNEL_ERROR_ILLEGAL_MASK" , 0x80020196 }, + { "SCE_KERNEL_ERROR_ILLEGAL_THID" , 0x80020197 }, + { "SCE_KERNEL_ERROR_UNKNOWN_THID" , 0x80020198 }, + { "SCE_KERNEL_ERROR_UNKNOWN_SEMID" , 0x80020199 }, + { "SCE_KERNEL_ERROR_UNKNOWN_EVFID" , 0x8002019a }, + { "SCE_KERNEL_ERROR_UNKNOWN_MBXID" , 0x8002019b }, + { "SCE_KERNEL_ERROR_UNKNOWN_VPLID" , 0x8002019c }, + { "SCE_KERNEL_ERROR_UNKNOWN_FPLID" , 0x8002019d }, + { "SCE_KERNEL_ERROR_UNKNOWN_MPPID" , 0x8002019e }, + { "SCE_KERNEL_ERROR_UNKNOWN_ALMID" , 0x8002019f }, + { "SCE_KERNEL_ERROR_UNKNOWN_TEID" , 0x800201a0 }, + { "SCE_KERNEL_ERROR_UNKNOWN_CBID" , 0x800201a1 }, + { "SCE_KERNEL_ERROR_DORMANT" , 0x800201a2 }, + { "SCE_KERNEL_ERROR_SUSPEND" , 0x800201a3 }, + { "SCE_KERNEL_ERROR_NOT_DORMANT" , 0x800201a4 }, + { "SCE_KERNEL_ERROR_NOT_SUSPEND" , 0x800201a5 }, + { "SCE_KERNEL_ERROR_NOT_WAIT" , 0x800201a6 }, + { "SCE_KERNEL_ERROR_CAN_NOT_WAIT" , 0x800201a7 }, + { "SCE_KERNEL_ERROR_WAIT_TIMEOUT" , 0x800201a8 }, + { "SCE_KERNEL_ERROR_WAIT_CANCEL" , 0x800201a9 }, + { "SCE_KERNEL_ERROR_RELEASE_WAIT" , 0x800201aa }, + { "SCE_KERNEL_ERROR_NOTIFY_CALLBACK" , 0x800201ab }, + { "SCE_KERNEL_ERROR_THREAD_TERMINATED" , 0x800201ac }, + { "SCE_KERNEL_ERROR_SEMA_ZERO" , 0x800201ad }, + { "SCE_KERNEL_ERROR_SEMA_OVF" , 0x800201ae }, + { "SCE_KERNEL_ERROR_EVF_COND" , 0x800201af }, + { "SCE_KERNEL_ERROR_EVF_MULTI" , 0x800201b0 }, + { "SCE_KERNEL_ERROR_EVF_ILPAT" , 0x800201b1 }, + { "SCE_KERNEL_ERROR_MBOX_NOMSG" , 0x800201b2 }, + { "SCE_KERNEL_ERROR_MPP_FULL" , 0x800201b3 }, + { "SCE_KERNEL_ERROR_MPP_EMPTY" , 0x800201b4 }, + { "SCE_KERNEL_ERROR_WAIT_DELETE" , 0x800201b5 }, + { "SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCK" , 0x800201b6 }, + { "SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE" , 0x800201b7 }, + { "SCE_KERNEL_ERROR_ILLEGAL_SPADADDR" , 0x800201b8 }, + { "SCE_KERNEL_ERROR_SPAD_INUSE" , 0x800201b9 }, + { "SCE_KERNEL_ERROR_SPAD_NOT_INUSE" , 0x800201ba }, + { "SCE_KERNEL_ERROR_ILLEGAL_TYPE" , 0x800201bb }, + { "SCE_KERNEL_ERROR_ILLEGAL_SIZE" , 0x800201bc }, + { "SCE_KERNEL_ERROR_ILLEGAL_COUNT" , 0x800201bd }, + { "SCE_KERNEL_ERROR_UNKNOWN_VTID" , 0x800201be }, + { "SCE_KERNEL_ERROR_ILLEGAL_VTID" , 0x800201bf }, + { "SCE_KERNEL_ERROR_ILLEGAL_KTLSID" , 0x800201c0 }, + { "SCE_KERNEL_ERROR_KTLS_FULL" , 0x800201c1 }, + { "SCE_KERNEL_ERROR_KTLS_BUSY" , 0x800201c2 }, + { "SCE_KERNEL_ERROR_PM_INVALID_PRIORITY" , 0x80020258 }, + { "SCE_KERNEL_ERROR_PM_INVALID_DEVNAME" , 0x80020259 }, + { "SCE_KERNEL_ERROR_PM_UNKNOWN_DEVNAME" , 0x8002025a }, + { "SCE_KERNEL_ERROR_PM_PMINFO_REGISTERED" , 0x8002025b }, + { "SCE_KERNEL_ERROR_PM_PMINFO_UNREGISTERED" , 0x8002025c }, + { "SCE_KERNEL_ERROR_PM_INVALID_MAJOR_STATE" , 0x8002025d }, + { "SCE_KERNEL_ERROR_PM_INVALID_REQUEST" , 0x8002025e }, + { "SCE_KERNEL_ERROR_PM_UNKNOWN_REQUEST" , 0x8002025f }, + { "SCE_KERNEL_ERROR_PM_INVALID_UNIT" , 0x80020260 }, + { "SCE_KERNEL_ERROR_PM_CANNOT_CANCEL" , 0x80020261 }, + { "SCE_KERNEL_ERROR_PM_INVALID_PMINFO" , 0x80020262 }, + { "SCE_KERNEL_ERROR_PM_INVALID_ARGUMENT" , 0x80020263 }, + { "SCE_KERNEL_ERROR_PM_ALREADY_TARGET_PWRSTATE" , 0x80020264 }, + { "SCE_KERNEL_ERROR_PM_CHANGE_PWRSTATE_FAILED" , 0x80020265 }, + { "SCE_KERNEL_ERROR_PM_CANNOT_CHANGE_DEVPWR_STATE" , 0x80020266 }, + { "SCE_KERNEL_ERROR_PM_NO_SUPPORT_DEVPWR_STATE" , 0x80020267 }, + { "SCE_KERNEL_ERROR_DMAC_REQUEST_FAILED" , 0x800202bc }, + { "SCE_KERNEL_ERROR_DMAC_REQUEST_DENIED" , 0x800202bd }, + { "SCE_KERNEL_ERROR_DMAC_OP_QUEUED" , 0x800202be }, + { "SCE_KERNEL_ERROR_DMAC_OP_NOT_QUEUED" , 0x800202bf }, + { "SCE_KERNEL_ERROR_DMAC_OP_RUNNING" , 0x800202c0 }, + { "SCE_KERNEL_ERROR_DMAC_OP_NOT_ASSIGNED" , 0x800202c1 }, + { "SCE_KERNEL_ERROR_DMAC_OP_TIMEOUT" , 0x800202c2 }, + { "SCE_KERNEL_ERROR_DMAC_OP_FREED" , 0x800202c3 }, + { "SCE_KERNEL_ERROR_DMAC_OP_USED" , 0x800202c4 }, + { "SCE_KERNEL_ERROR_DMAC_OP_EMPTY" , 0x800202c5 }, + { "SCE_KERNEL_ERROR_DMAC_OP_ABORTED" , 0x800202c6 }, + { "SCE_KERNEL_ERROR_DMAC_OP_ERROR" , 0x800202c7 }, + { "SCE_KERNEL_ERROR_DMAC_CHANNEL_RESERVED" , 0x800202c8 }, + { "SCE_KERNEL_ERROR_DMAC_CHANNEL_EXCLUDED" , 0x800202c9 }, + { "SCE_KERNEL_ERROR_DMAC_PRIVILEGE_ADDRESS" , 0x800202ca }, + { "SCE_KERNEL_ERROR_DMAC_NO_ENOUGHSPACE" , 0x800202cb }, + { "SCE_KERNEL_ERROR_DMAC_CHANNEL_NOT_ASSIGNED" , 0x800202cc }, + { "SCE_KERNEL_ERROR_DMAC_CHILD_OPERATION" , 0x800202cd }, + { "SCE_KERNEL_ERROR_DMAC_TOO_MUCH_SIZE" , 0x800202ce }, + { "SCE_KERNEL_ERROR_DMAC_INVALID_ARGUMENT" , 0x800202cf }, + { "SCE_KERNEL_ERROR_MFILE" , 0x80020320 }, + { "SCE_KERNEL_ERROR_NODEV" , 0x80020321 }, + { "SCE_KERNEL_ERROR_XDEV" , 0x80020322 }, + { "SCE_KERNEL_ERROR_BADF" , 0x80020323 }, + { "SCE_KERNEL_ERROR_INVAL" , 0x80020324 }, + { "SCE_KERNEL_ERROR_UNSUP" , 0x80020325 }, + { "SCE_KERNEL_ERROR_ALIAS_USED" , 0x80020326 }, + { "SCE_KERNEL_ERROR_CANNOT_MOUNT" , 0x80020327 }, + { "SCE_KERNEL_ERROR_DRIVER_DELETED" , 0x80020328 }, + { "SCE_KERNEL_ERROR_ASYNC_BUSY" , 0x80020329 }, + { "SCE_KERNEL_ERROR_NOASYNC" , 0x8002032a }, + { "SCE_KERNEL_ERROR_REGDEV" , 0x8002032b }, + { "SCE_KERNEL_ERROR_NOCWD" , 0x8002032c }, + { "SCE_KERNEL_ERROR_NAMETOOLONG" , 0x8002032d }, + { "SCE_KERNEL_ERROR_NXIO" , 0x800203e8 }, + { "SCE_KERNEL_ERROR_IO" , 0x800203e9 }, + { "SCE_KERNEL_ERROR_NOMEM" , 0x800203ea }, + { "SCE_KERNEL_ERROR_STDIO_NOT_OPENED" , 0x800203eb }, + { "SCE_KERNEL_ERROR_CACHE_ALIGNMENT" , 0x8002044c }, + { "SCE_KERNEL_ERROR_ERRORMAX" , 0x8002044d }, + { NULL, 0 }, +}; diff --git a/pspsh/pspkerror.h b/pspsh/pspkerror.h new file mode 100644 index 0000000..5864618 --- /dev/null +++ b/pspsh/pspkerror.h @@ -0,0 +1,26 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * pspkerror.h - Definitions for error codes + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/pspsh/pspkerror.h $ + * $Id: pspkerror.h 2066 2006-11-15 21:09:34Z tyranid $ + */ +#ifndef PSPKERROR_H +#define PSPKERROR_H + +#include + +struct PspErrorCode +{ + const char *name; + unsigned int num; +}; + +extern struct PspErrorCode PspKernelErrorCodes[]; + +#endif /* PSPKERROR_H */ diff --git a/pspsh/pspkerror.o b/pspsh/pspkerror.o new file mode 100644 index 0000000..e0fddea Binary files /dev/null and b/pspsh/pspkerror.o differ diff --git a/pspsh/pspsh b/pspsh/pspsh new file mode 100755 index 0000000..08656c4 Binary files /dev/null and b/pspsh/pspsh differ diff --git a/pspsh/pspsh.C b/pspsh/pspsh.C new file mode 100644 index 0000000..0b13e74 --- /dev/null +++ b/pspsh/pspsh.C @@ -0,0 +1,2044 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * pspsh.c - PSPLINK pc terminal + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/pspsh/pspsh.C $ + * $Id: pspsh.C 2200 2007-03-08 21:21:20Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "parse_args.h" +#include "pspkerror.h" +#include "disasm.h" +#include "asm.h" + +#ifndef SOL_TCP +#define SOL_TCP 6 +#endif + +#define DEFAULT_PORT 10000 +#define HISTORY_FILE ".pspsh.hist" +#define DEFAULT_IP "localhost" + +struct Args +{ + const char *ip; + const char *hist; + unsigned short port; + /* Indicates we are in script mode */ + int script; + /* Holds the current execution line */ + char exec[1024]; + /* Script arguments */ + int sargc; + char **sargv; + int notty; +}; + +struct GlobalContext +{ + struct Args args; + int exit; + int conn_sanity; + fd_set readsave; + int sock; + int outsock; + int errsock; + int fssock; + char history_file[PATH_MAX]; + char currpath[PATH_MAX]; + char currcmd[PATH_MAX]; + FILE *fredir; + FILE *fscript; + char **sargv; + int sargc; + int lasterr; + FILE *fstdout; + FILE *fstderr; + int asmmode; + unsigned int asmaddr; + int ttymode; +}; + +struct GlobalContext g_context; +static int g_verbose = 0; +extern char **environ; + +void shutdown_app(void); +int help_cmd(int argc, char **argv); +int close_cmd(int argc, char **argv); +int exit_cmd(int argc, char **argv); +int asm_cmd(int argc, char **argv); +int env_cmd(int argc, char **argv); +int set_cmd(int argc, char **argv); +int unset_cmd(int argc, char **argv); +int echo_cmd(int argc, char **argv); +int error_cmd(int argc, char **argv); +int strlen_cmd(int argc, char **argv); +int disset_cmd(int argc, char **argv); +int disclear_cmd(int argc, char **argv); +int disopts_cmd(int argc, char **argv); +int tty_cmd(int argc, char **argv); +int symload_cmd(int argc, char **argv); +void cli_handler(char *buf); +struct TabEntry* read_tab_completion(void); +struct TabEntry +{ + struct TabEntry *pNext; + char *name; +}; + +struct sh_command g_commands[] = { SHELL_COMMANDS }; + +const char *g_drive_prefix[] = { + "ms0:/", + "flash0:/", + "flash1:/", + "disc0:/", + "host0:/", + "host1:/", + "host2:/", + "host3:/", + "host4:/", + "host5:/", + "host6:/", + "host7:/", + NULL +}; + +/* Find a command from the command list */ +const struct sh_command* find_command(const char *cmd) +{ + const struct sh_command* found_cmd = NULL; + int cmd_loop; + + for(cmd_loop = 0; g_commands[cmd_loop].name != NULL; cmd_loop++) + { + if(strcmp(cmd, g_commands[cmd_loop].name) == 0) + { + found_cmd = &g_commands[cmd_loop]; + break; + } + + if(g_commands[cmd_loop].syn) + { + if(strcmp(cmd, g_commands[cmd_loop].syn) == 0) + { + found_cmd = &g_commands[cmd_loop]; + break; + } + } + } + + return found_cmd; +} + + +int fixed_write(int s, const void *buf, int len) +{ + int written = 0; + + while(written < len) + { + int ret; + + ret = write(s, (const char*) buf+written, len-written); + if(ret < 0) + { + if(errno != EINTR) + { + perror("write"); + written = -1; + break; + } + } + else + { + written += ret; + } + } + + return written; +} + +int execute_line(const char *buf) +{ + char args[4096]; + char *argv[16]; + int argc; + int len; + int ret = 0; + + len = strlen(buf); + + if(len > 0) + { + char redir[PATH_MAX]; + int type; + int binlen = parse_cli(buf, args, &argc, argv, 16, g_context.sargc, g_context.sargv, &type, redir); + if((binlen > 0) && (args[0] != '#')) + { + if(strchr(argv[0], '.') || strchr(argv[0], '/')) + { + int ldlen = strlen("ld")+1; + /* If this looks to be a path then prefix ld and send */ + memmove(args+ldlen, args, binlen); + memcpy(args, "ld", ldlen); + binlen += ldlen; + } + else + { + const struct sh_command *cmd = find_command(argv[0]); + if((cmd) && (cmd->func)) + { + if(cmd->min_args > (argc-1)) + { + help_cmd(argc, argv); + return 0; + } + else + { + if(!cmd->func(argc-1, argv+1)) + { + /* If it returns 0 then dont continue with the output */ + return 0; + } + } + } + } + + if(!g_context.args.script) + { + /* Remove the handler and prompt */ + rl_callback_handler_remove(); + rl_callback_handler_install("", cli_handler); + } + + if(type != REDIR_TYPE_NONE) + { + if(type == REDIR_TYPE_NEW) + { + g_context.fredir = fopen(redir, "w"); + } + else + { + g_context.fredir = fopen(redir, "a"); + } + + if(g_context.fredir == NULL) + { + fprintf(stderr, "Warning: Could not open file %s\n", redir); + } + } + + len = fixed_write(g_context.sock, args, binlen); + if(len < 0) + { + close(g_context.sock); + g_context.sock = -1; + return -1; + } + strcpy(g_context.currcmd, args); + ret = 1; + } + } + + return ret; +} + +void close_script(void) +{ + if(g_context.fscript) + { + if(g_context.fscript != stdin) + { + fclose(g_context.fscript); + } + g_context.fscript = NULL; + } + + if(g_context.sargv) + { + int i; + + for(i = 0; i < g_context.sargc; i++) + { + if(g_context.sargv[i]) + { + free(g_context.sargv[i]); + } + } + free(g_context.sargv); + g_context.sargv = NULL; + } + + g_context.sargc = 0; + + if(g_context.args.script) + { + g_context.exit = 1; + } +} + +int open_script(const char *file, int sargc, char **sargv) +{ + int ret = 0; + int i; + + do + { + /* Ensure script is closed */ + close_script(); + + if(strcmp(file, "-") == 0) + { + g_context.fscript = stdin; + } + else + { + g_context.fscript = fopen(file, "r"); + if(g_context.fscript == NULL) + { + fprintf(stderr, "Could not open script file %s\n", file); + break; + } + } + + if(sargc > 0) + { + g_context.sargv = (char**) malloc(sizeof(char*)*sargc); + if(g_context.sargv == NULL) + { + fprintf(stderr, "Could not allocate script arguments\n"); + break; + } + for(i = 0; i < sargc; i++) + { + g_context.sargv[i] = strdup(sargv[i]); + if(g_context.sargv[i] == NULL) + { + fprintf(stderr, "Could not allocate argument string %d\n", i); + break; + } + } + + if(i < sargc) + { + break; + } + + g_context.sargc = sargc; + } + + ret = 1; + } + while(0); + + if(!ret) + { + close_script(); + } + + return ret; +} + +int in_script(void) +{ + if(g_context.args.exec[0]) + { + return 1; + } + + if(g_context.fscript) + { + return 1; + } + + return 0; +} + +int execute_script_line(void) +{ + char line[1024]; + + if(g_context.args.exec[0]) + { + int ret = execute_line(g_context.args.exec); + g_context.args.exec[0] = ' '; + g_context.args.exec[1] = 0; + if(ret < 0) + { + return -1; + } + + if(ret > 0) + { + return 1; + } + + /* Only way we get here is if there were no lines to execute */ + close_script(); + } + else if(g_context.fscript) + { + while(fgets(line, sizeof(line), g_context.fscript)) + { + int ret = execute_line(line); + if(ret < 0) + { + return -1; + } + + /* Only return if we have sent a line to the PSP */ + if(ret > 0) + { + return 1; + } + } + + /* Only way we get here is if there were no lines to execute */ + close_script(); + } + + return 0; +} + +int close_cmd(int argc, char **argv) +{ + if(g_context.args.script == 0) + { + rl_callback_handler_remove(); + rl_callback_handler_install("", cli_handler); + } + g_context.exit = 1; + return 0; +} + +int strlen_cmd(int argc, char **argv) +{ + char val[32]; + + snprintf(val, sizeof(val), "%d", strlen(argv[0])); + setenv("?", val, 1); + + return 0; +} + +int disopts_cmd(int argc, char **argv) +{ + if(argc == 0) + { + disasmPrintOpts(); + } + else + { + disasmSetOpts(argv[0]); + } + + return 0; +} + +int tty_cmd(int argc, char **argv) +{ + g_context.ttymode = 1; + rl_callback_handler_remove(); + rl_callback_handler_install("", cli_handler); + + return 0; +} + +int execute_script(const char *cmd) +{ + char args[4096]; + char *argv[16]; + int argc; + int len; + + len = strlen(cmd); + + if(len > 0) + { + char redir[PATH_MAX]; + int type; + int binlen = parse_cli(cmd, args, &argc, argv, 16, 0, NULL, &type, redir); + if(binlen > 0) + { + if(open_script(argv[0], argc, argv)) + { + if(execute_script_line() > 0) + { + return 1; + } + } + } + } + + return 0; +} + +int asm_cmd(int argc, char **argv) +{ + char cmd[1024]; + + if(argc > 1) + { + int i; + char *curr; + + curr = cmd; + snprintf(cmd, sizeof(cmd), "pokew '%s' ", argv[0]); + curr += strlen(cmd); + for(i = 1; i < argc; i++) + { + unsigned int opcode; + if(asmAssemble(argv[i], 0, &opcode) == 0) + { + sprintf(curr, "0x%08X ", opcode); + curr += strlen(curr); + } + else + { + break; + } + } + + if(i == argc) + { + execute_line(cmd); + } + } + else + { + snprintf(cmd, sizeof(cmd), "calc '%s'", argv[0]); + execute_line(cmd); + g_context.asmmode = 1; + g_context.asmaddr = 0; + } + + return 0; +} + +int symload_cmd(int argc, char **argv) +{ + /* If no args or this is for a module name then issue direct */ + if((argc == 0) || (argv[0][0] == '@')) + { + return 1; + } + else + { + /* This is probably a file, try and open it, if successful issue a symload command + * with the appropriate module name to fixup the address */ + } + + return 0; +} + +int exit_cmd(int argc, char **argv) +{ + g_context.exit = 1; + return 1; +} + +void cli_handler(char *buf) +{ + if(buf) + { + if(g_context.ttymode) + { + if(strncmp(buf, "~.", 2) == 0) + { + char prompt[PATH_MAX]; + + g_context.ttymode = 0; + rl_callback_handler_remove(); + snprintf(prompt, PATH_MAX, "%s> ", g_context.currpath); + rl_callback_handler_install(prompt, cli_handler); + } + else if(g_context.outsock >= 0) + { + char b[1024]; + + snprintf(b, sizeof(b), "%s\n", buf); + write(g_context.outsock, b, strlen(b)); + } + + return; + } + + while(isspace(*buf)) + { + buf++; + } + + if(*buf == 0) + { + if(g_context.asmmode) + { + char prompt[PATH_MAX]; + g_context.asmmode = 0; + g_context.asmaddr = 0; + rl_callback_handler_remove(); + snprintf(prompt, PATH_MAX, "%s> ", g_context.currpath); + rl_callback_handler_install(prompt, cli_handler); + } + return; + } + + if(in_script()) + { + /* When a script is running only accept stop */ + if(strcmp(buf, "stop") == 0) + { + close_script(); + } + + return; + } + + if(g_context.asmmode) + { + char cmd[1024]; + /* Do assembler */ + unsigned int opcode; + if(asmAssemble(buf, g_context.asmaddr, &opcode) == 0) + { + sprintf(cmd, "pokew 0x%08X 0x%08X ", g_context.asmaddr, opcode); + execute_line(cmd); + } + + return; + } + + add_history(buf); + if(buf[0] == '!') + { + if(strncmp(&buf[1], "cd ", 3) == 0) + { + chdir(&buf[4]); + } + else + { + system(&buf[1]); + } + return; + } + else if(buf[0] == '@') + { + if(g_context.fssock >= 0) + { + (void) write(g_context.fssock, &buf[1], strlen(&buf[1])); + } + return; + } + else if(buf[0] == '%') + { + execute_script(&buf[1]); + return; + } + + execute_line(buf); + } + else + { + shutdown_app(); + exit(0); + } +} + +int cli_reset(int a, int b) +{ + execute_line("reset"); + + return 0; +} + +int cli_step(int a, int b) +{ + execute_line("step"); + + return 0; +} + +int cli_skip(int a, int b) +{ + execute_line("skip"); + + return 0; +} + +/* Complete a command name */ +char *command_gen(const char *text, int state) +{ + static int cmdno; + static int driveno; + static int locals; + static int len; + + if(state == 0) + { + cmdno = 0; + driveno = 0; + locals = 0; + len = strlen(text); + } + + while(g_commands[cmdno].name) + { + const char *name = g_commands[cmdno].name; + const char *syn = g_commands[cmdno].syn; + + cmdno++; + + if(strncmp(name, text, len) == 0) + { + return strdup(name); + } + else if((syn) && (strncmp(syn, text, len) == 0)) + { + return strdup(syn); + } + } + + while(g_drive_prefix[driveno]) + { + const char *name = g_drive_prefix[driveno]; + + driveno++; + if(strncmp(name, text, len) == 0) + { + rl_completion_append_character = 0; + return strdup(name); + } + } + + return NULL; +} + +struct TabEntry* add_drive_prefixes(const char *text, struct TabEntry *pEntry) +{ + struct TabEntry head; + struct TabEntry *pCurr; + int len; + int i; + + len = strlen(text); + pCurr = &head; + memset(&head, 0, sizeof(head)); + i = 0; + while(g_drive_prefix[i]) + { + if(strncmp(g_drive_prefix[i], text, len) == 0) + { + pCurr->pNext = (struct TabEntry *) malloc(sizeof(struct TabEntry)); + memset(pCurr->pNext, 0, sizeof(struct TabEntry)); + pCurr->pNext->name = strdup(g_drive_prefix[i]); + pCurr = pCurr->pNext; + } + i++; + } + + if(head.pNext) + { + pCurr->pNext = pEntry; + return head.pNext; + } + + return pEntry; +} + +/* Complete a file name */ +char *filename_gen(const char *text, int state) +{ + static char dirpath[PATH_MAX]; + static struct TabEntry *pEntry = NULL; + char *name; + + name = NULL; + if(state == 0) + { + char cmd[PATH_MAX*2+5]; + char *curr; + char filepath[PATH_MAX]; + char *slash; + + /* Free list if it exists */ + while(pEntry) + { + struct TabEntry *pTemp; + pTemp = pEntry->pNext; + free(pEntry->name); + free(pEntry); + pEntry = pTemp->pNext; + } + + strcpy(dirpath, text); + memset(filepath, 0, sizeof(filepath)); + slash = strrchr(dirpath, '/'); + if(slash) + { + slash++; + strcpy(filepath, slash); + *slash = 0; + } + else + { + strcpy(filepath, dirpath); + strcpy(dirpath, ""); + } + + curr = cmd; + strcpy(curr, "tab"); + curr += strlen(curr)+1; + strcpy(curr, dirpath); + curr += strlen(curr)+1; + if(filepath[0]) + { + strcpy(curr, filepath); + curr += strlen(curr)+1; + } + *curr++ = 1; + write(g_context.sock, cmd, curr-cmd); + pEntry = read_tab_completion(); + if(pEntry == NULL) + { + pEntry = add_drive_prefixes(text, pEntry); + dirpath[0] = 0; + filepath[0] = 0; + } + } + + if(pEntry) + { + struct TabEntry *pNext; + + name = (char*) malloc(strlen(dirpath) + strlen(pEntry->name) + 1); + if(name) + { + sprintf(name, "%s%s", dirpath, pEntry->name); + } + pNext = pEntry->pNext; + free(pEntry->name); + free(pEntry); + pEntry = pNext; + if(name[strlen(name)-1] == '/') + { + rl_completion_append_character = 0; + } + else + { + rl_completion_append_character = ' '; + } + } + + return name; +} + +/* Complete a uid name */ +char *uid_gen(const char *text, int state) +{ + static struct TabEntry *pEntry = NULL; + char *name; + + name = NULL; + if(state == 0) + { + char cmd[PATH_MAX*2+5]; + char *curr; + + /* Free list if it exists */ + while(pEntry) + { + struct TabEntry *pTemp; + pTemp = pEntry->pNext; + free(pEntry->name); + free(pEntry); + pEntry = pTemp->pNext; + } + + curr = cmd; + strcpy(curr, "tab"); + curr += strlen(curr)+1; + strcpy(curr, text); + curr += strlen(curr)+1; + *curr++ = 1; + write(g_context.sock, cmd, curr-cmd); + pEntry = read_tab_completion(); + } + + if(pEntry) + { + struct TabEntry *pNext; + + name = (char*) malloc(strlen(pEntry->name) + 1); + if(name) + { + sprintf(name, "%s", pEntry->name); + } + pNext = pEntry->pNext; + free(pEntry->name); + free(pEntry); + pEntry = pNext; + rl_completion_append_character = ' '; + } + + return name; +} + +/* Completion display, get readline to still do most of the work for us */ +void completion_display(char **matches, int num, int max) +{ + int temp = rl_filename_completion_desired; + + rl_filename_completion_desired = 1; + rl_display_match_list(matches, num, max); + rl_forced_update_display (); + rl_filename_completion_desired = temp; +} + +char** shell_completion(const char *text, int start, int end) +{ + char **matches = (char**) NULL; + char *curr_line = rl_line_buffer; + + while(isspace(*curr_line)) + { + curr_line++; + } + + rl_completion_append_character = ' '; + rl_completion_display_matches_hook = NULL; + + /* Find if this is current in shell or usbhostfs modes */ + if((*curr_line == '!') || (*curr_line == '@') || (*curr_line == '%')) + { + /* Do normal (local) filename completion */ + matches = rl_completion_matches(text, rl_filename_completion_function); + } + else if(start == (curr_line-rl_line_buffer)) /* If this is the first word in the list */ + { + if(strchr(text, '.') || strchr(text, '/')) + { + rl_completion_display_matches_hook = completion_display; + matches = rl_completion_matches(text, filename_gen); + } + else + { + matches = rl_completion_matches(text, command_gen); + } + } + else + { + rl_completion_display_matches_hook = completion_display; + if(text[0] == '@') + { + matches = rl_completion_matches(text, uid_gen); + } + else + { + matches = rl_completion_matches(text, filename_gen); + } + } + + return matches; +} + +int init_readline(void) +{ +#if RL_READLINE_VERSION >= 0x500 + rl_bind_key_in_map(META('r'), cli_reset, emacs_standard_keymap); + rl_bind_key_in_map(META('s'), cli_step, emacs_standard_keymap); + rl_bind_key_in_map(META('k'), cli_skip, emacs_standard_keymap); +#endif + rl_attempted_completion_function = shell_completion; + rl_callback_handler_install("", cli_handler); + rl_basic_word_break_characters = "\t\n "; + rl_completer_word_break_characters = "\t\n "; + + return 1; +} + +/* Check if we were symlinked, if so then build an exec command */ +int check_symlink(const char *name, int argc, char **argv, struct Args *args) +{ + int ret = 0; + const char *cmd; + char *curr; + int len; + + cmd = strrchr(name, '/'); + if(!cmd) + { + cmd = name; + } + else + { + cmd++; + } + + if((strcmp(cmd, "pspsh")) && (strncmp(cmd, "psp", 3) == 0)) + { + int currlen; + cmd += 3; + + curr = args->exec; + len = sizeof(args->exec); + currlen = snprintf(curr, len, "%s", cmd); + if(currlen > 0) + { + int i; + + args->script = 1; + args->notty = 1; + len -= currlen; + curr += currlen; + for(i = 0; i < argc; i++) + { + currlen = snprintf(curr, len, " '%s'", argv[i]); + if(currlen <= 0) + { + break; + } + len -= currlen; + curr += currlen; + } + } + ret = 1; + } + + return ret; +} + +void dump_symlinks(int remove) +{ + int cmd_loop; + + printf("#!/bin/sh\n\n"); + + for(cmd_loop = 0; g_commands[cmd_loop].name; cmd_loop++) + { + if(g_commands[cmd_loop].help != NULL) + { + if(!remove) + { + printf("ln -s pspsh psp%s\n", g_commands[cmd_loop].name); + } + else + { + printf("rm psp%s\n", g_commands[cmd_loop].name); + } + if((g_commands[cmd_loop].syn) && (g_commands[cmd_loop].syn[0] != '?')) + { + if(!remove) + { + printf("ln -s pspsh psp%s\n", g_commands[cmd_loop].syn); + } + else + { + printf("rm psp%s\n", g_commands[cmd_loop].syn); + } + } + } + } +} + +int parse_args(int argc, char **argv, struct Args *args) +{ + const char *name; + memset(args, 0, sizeof(*args)); + args->port = DEFAULT_PORT; + args->ip = DEFAULT_IP; + + if(argc == 0) + { + return 0; + } + + name = argv[0]; + + while(1) + { + int ch; + int error = 0; + + ch = getopt(argc, argv, "nsrp:h:i:e:"); + if(ch < 0) + { + break; + } + + switch(ch) + { + case 'p': args->port = atoi(optarg); + break; + case 'h': args->hist = optarg; + break; + case 'n': args->notty = 1; + break; + case 'i': args->ip = optarg; + break; + case 'e': snprintf(args->exec, sizeof(args->exec), "%s", optarg); + args->script = 1; + args->notty = 1; + break; + case 's': dump_symlinks(0); + exit(0); + break; + case 'r': dump_symlinks(1); + exit(0); + break; + case 'v': g_verbose = 1; + break; + default : error = 1; + break; + }; + + if(error) + { + return 0; + } + } + + argc -= optind; + argv += optind; + + if(!check_symlink(name, argc, argv, args)) + { + if(argc > 0) + { + if(!open_script(argv[0], argc, argv)) + { + return 0; + } + args->script = 1; + args->notty = 1; + } + } + + return 1; +} + +void print_help(void) +{ + fprintf(stderr, "PSPSH Help (c) TyRaNiD\n"); + fprintf(stderr, "Built %s %s - $Revision: 2200 $\n", __DATE__, __TIME__); + fprintf(stderr, "Usage: pspsh [options] [script args...]\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "-i ipaddr : Specify the IP address to connect to\n"); + fprintf(stderr, "-p port : Specify the port number\n"); + fprintf(stderr, "-h history : Specify the history file (default ~/%s)\n", HISTORY_FILE); + fprintf(stderr, "-e cmd : Execute a command and exit\n"); + fprintf(stderr, "-n : Do not connect up the tty (stdin/stdout/stderr)\n"); + fprintf(stderr, "-v : Verbose mode\n"); +} + +int init_sockaddr(struct sockaddr_in *name, const char *ipaddr, unsigned short port) +{ + struct hostent *hostinfo; + + name->sin_family = AF_INET; + name->sin_port = htons(port); + hostinfo = gethostbyname(ipaddr); + if(hostinfo == NULL) + { + fprintf(stderr, "Unknown host %s\n", ipaddr); + return 0; + } + name->sin_addr = *(struct in_addr *) hostinfo->h_addr; + + return 1; +} + +int help_cmd(int argc, char **argv) +{ + int cmd_loop; + + if(argc < 1) + { + fprintf(stderr, "Command Categories\n\n"); + for(cmd_loop = 0; g_commands[cmd_loop].name; cmd_loop++) + { + if(g_commands[cmd_loop].help == NULL) + { + fprintf(stderr, "%-10s - %s\n", g_commands[cmd_loop].name, g_commands[cmd_loop].desc); + } + } + fprintf(stderr, "\nType 'help category' for more information\n"); + } + else + { + const struct sh_command* found_cmd; + + found_cmd = find_command(argv[0]); + if((found_cmd != NULL) && (found_cmd->desc)) + { + if(found_cmd->help == NULL) + { + /* Print the commands listed under the separator */ + fprintf(stderr, "Category %s\n\n", found_cmd->name); + for(cmd_loop = 1; found_cmd[cmd_loop].name && found_cmd[cmd_loop].help != NULL; cmd_loop++) + { + if(found_cmd[cmd_loop].desc) + { + fprintf(stderr, "%-10s - %s\n", found_cmd[cmd_loop].name, found_cmd[cmd_loop].desc); + } + } + } + else + { + fprintf(stderr, "Command: %s\t\n", found_cmd->name); + if(found_cmd->syn) + { + fprintf(stderr, "Synonym: %s\n", found_cmd->syn); + } + fprintf(stderr, "Usage: %s %s\n", found_cmd->name, found_cmd->help); + fprintf(stderr, "%s\n\n", found_cmd->desc); + if(found_cmd->detail[0]) + { + fprintf(stderr, "Detail:\n"); + fprintf(stderr, "%s\n", found_cmd->detail); + } + } + } + else + { + fprintf(stderr, "Unknown command %s, type help for information\n", argv[0]); + } + } + + return 0; +} + +int env_cmd(int argc, char **argv) +{ + int i = 0; + while(environ[i]) + { + printf("%s\n", environ[i]); + i++; + } + + return 0; +} + +int set_cmd(int argc, char **argv) +{ + char *name; + char *value; + int i; + + do + { + name = argv[0]; + value = strchr(argv[0], '='); + if(value) + { + *value++ = 0; + } + else + { + printf("Error, no value specified\n"); + break; + } + + if(argv[0][0] == 0) + { + printf("Error, no name specified\n"); + break; + } + + if(!isalpha(name[0])) + { + printf("Error, variable names must start with a letter\n"); + break; + } + + i = 0; + while(name[i]) + { + if(!isalnum(name[i])) + { + printf("Error, variable names must be alphanumeric\n"); + break; + } + i++; + } + + if(setenv(name, value, 1) < 0) + { + perror("setenv"); + break; + } + } + while(0); + + return 0; +} + +int unset_cmd(int argc, char **argv) +{ + (void) unsetenv(argv[0]); + + return 0; +} + +int error_cmd(int argc, char **argv) +{ + unsigned int err; + unsigned int facility; + int i; + + err = strtoul(argv[0], NULL, 0); + facility = err >> 16; + + if(facility == 0x8002) + { + i = 0; + while(PspKernelErrorCodes[i].name) + { + if(err == PspKernelErrorCodes[i].num) + { + printf("Error: %s\n", PspKernelErrorCodes[i].name); + return 0; + } + i++; + } + } + else if(facility == 0x8001) + { + printf("Error: %s\n", strerror(err & 0xFFFF)); + return 0; + } + + printf("Unknown error code 0x%08X\n", err); + + return 0; +} + +int echo_cmd(int argc, char **argv) +{ + int i; + for(i = 0; i < argc; i++) + { + printf("%s", argv[i]); + if(i < (argc-1)) + { + printf(" "); + } + } + printf("\n"); + + return 0; +} + +int process_cmd(const unsigned char *str) +{ + if(*str < 128) + { + if(g_context.fredir) + { + fprintf(g_context.fredir, "%s", str); + } + else + { + printf("%s", str); + } + } + else + { + if(*str == SHELL_CMD_CWD) + { + snprintf(g_context.currpath, PATH_MAX, "%s", str+1); + (void) setenv("PSPPWD", g_context.currpath, 1); + } + else if((*str == SHELL_CMD_SUCCESS) || (*str == SHELL_CMD_ERROR)) + { + char prompt[PATH_MAX]; + + if(*str == SHELL_CMD_ERROR) + { + /* If there was a command then print the help */ + if(g_context.currcmd[0]) + { + const struct sh_command *cmd = find_command(g_context.currcmd); + if(cmd == NULL) + { + fprintf(stderr, "Unknown command %s\n", g_context.currcmd); + } + else + { + if(cmd->help) + { + fprintf(stderr, "Usage: %s\n", cmd->help); + } + else + { + fprintf(stderr, "Command %s has no help associated\n", g_context.currcmd); + } + } + } + + /* On error stop any executing script */ + if(in_script()) + { + close_script(); + } + + /* Set return code */ + g_context.lasterr = 1; + } + else + { + g_context.lasterr = 0; + } + + (void) setenv("?", (char*) (str+1), 1); + g_context.currcmd[0] = 0; + if(g_context.fredir) + { + fclose(g_context.fredir); + g_context.fredir = NULL; + } + else + { + fflush(stdout); + } + + /* Only restore if there is no pending script and we didn't execute a line */ + if((execute_script_line() <= 0) && (g_context.args.script == 0)) + { + if(g_context.asmmode) + { + if(!g_context.asmaddr) + { + g_context.asmaddr = strtoul((char*) str+1, NULL, 0); + } + else + { + g_context.asmaddr += 4; + } + } + + rl_callback_handler_remove(); + if(g_context.asmmode) + { + snprintf(prompt, PATH_MAX, "asm:0x%08X> ", g_context.asmaddr); + } + else + { + snprintf(prompt, PATH_MAX, "%s> ", g_context.currpath); + g_context.ttymode = 0; + } + rl_callback_handler_install(prompt, cli_handler); + } + } + else if(*str == SHELL_CMD_TAB) + { + fprintf(stderr, "Mismatched Tab Match: %s\n", str+1); + } + else if(*str == SHELL_CMD_DISASM) + { + unsigned int addr; + unsigned int opcode; + char *endp; + + str++; + addr = strtoul((char*) str, &endp, 16); + if(*endp != ':') + { + fprintf(stderr, "Invalid disasm command %s\n", str); + } + else + { + opcode = strtoul(endp+1, NULL, 16); + printf("%s\n", disasmInstruction(opcode, addr, NULL, NULL, 0)); + } + } + else if(*str == SHELL_CMD_SYMLOAD) + { + char sha1[41]; + char addr[9]; + const char *name; + + str++; + if(strlen((const char*) str) > (40+8)) + { + memcpy(sha1, str, 40); + sha1[40] = 0; + memcpy(addr, &str[40], 8); + addr[8] = 0; + name = (const char*) &str[40+8]; + printf("Symbol Load - Name %32s, Address 0x%s, SHA1 %s\n", name, addr, sha1); + } + } + } + + return 1; +} + +int read_socket(int sock) +{ + static unsigned char linebuf[16*1024]; + static int pos = 0; + unsigned char buf[1024]; + unsigned char *curr; + int len; + + len = read(sock, buf, sizeof(buf)-1); + if(len < 0) + { + perror("read"); + return -1; + } + + /* EOF */ + if(len == 0) + { + return -1; + } + + buf[len] = 0; + curr = buf; + + while(*curr) + { + if(*curr == SHELL_CMD_BEGIN) + { + /* Reset start pos, discard any data we ended up missing */ + pos = 1; + } + else if(*curr == SHELL_CMD_END) + { + if(pos > 1) + { + linebuf[pos-1] = 0; + /* Found a command, process */ + process_cmd(linebuf); + pos = 0; + } + } + else + { + if(pos > 0) + { + linebuf[pos-1] = *curr; + pos++; + } + } + + curr++; + } + + return len; +} + +int read_outsocket(int sock) +{ + char buf[1024]; + int len; + + len = read(sock, buf, sizeof(buf)-1); + if(len < 0) + { + perror("read"); + return -1; + } + + /* EOF */ + if(len == 0) + { + return -1; + } + + buf[len] = 0; + + fprintf(g_context.fstdout, "%s", buf); + fflush(g_context.fstdout); + + return len; +} + +int read_errsocket(int sock) +{ + char buf[1024]; + int len; + + len = read(sock, buf, sizeof(buf)-1); + if(len < 0) + { + perror("read"); + return -1; + } + + /* EOF */ + if(len == 0) + { + return -1; + } + + buf[len] = 0; + + fprintf(g_context.fstderr, "%s", buf); + fflush(g_context.fstderr); + + return len; +} + +int read_fssocket(int sock) +{ + char buf[1024]; + int len; + + len = read(sock, buf, sizeof(buf)-1); + if(len < 0) + { + perror("read"); + return -1; + } + + /* EOF */ + if(len == 0) + { + return -1; + } + + buf[len] = 0; + + fprintf(stderr, "%s", buf); + + return len; +} + +int connect_to(const char *ipaddr, unsigned short port) +{ + int sock = -1; + int success = 0; + struct sockaddr_in name; + + do + { + if(!init_sockaddr(&name, ipaddr, port)) + { + break; + } + sock = socket(PF_INET, SOCK_STREAM, 0); + if(sock < 0) + { + perror("socket"); + break; + } + + if(connect(sock, (struct sockaddr *) &name, sizeof(name)) < 0) + { + perror("connect"); + break; + } + else + { + FD_SET(sock, &g_context.readsave); + } + + success = 1; + } + while(0); + + if(!success) + { + if(sock >= 0) + { + close(sock); + sock = -1; + } + } + + return sock; +} + +int read_tab(int sock, struct TabEntry *head) +{ + static unsigned char linebuf[16*1024]; + static int pos = 0; + unsigned char buf[1024]; + unsigned char *curr; + int len; + int ret = 0; + struct TabEntry *tabcurr; + + if(head == NULL) + { + fprintf(stderr, "Internal Error (head==NULL)\n"); + return -1; + } + + tabcurr = head; + while(tabcurr->pNext) + { + tabcurr = tabcurr->pNext; + } + + len = read(sock, buf, sizeof(buf)-1); + if(len < 0) + { + perror("read"); + return -1; + } + + /* EOF */ + if(len == 0) + { + return -1; + } + + buf[len] = 0; + curr = buf; + + while(*curr) + { + if(*curr == SHELL_CMD_BEGIN) + { + /* Reset start pos, discard any data we ended up missing */ + pos = 1; + } + else if(*curr == SHELL_CMD_END) + { + if(pos > 1) + { + linebuf[pos-1] = 0; + /* Found a command, process */ + if(linebuf[0] < 128) + { + printf("%s", linebuf); + fflush(stdout); + } + else if((linebuf[0] == SHELL_CMD_SUCCESS) || (linebuf[0] == SHELL_CMD_ERROR)) + { + ret = -1; + } + else if(linebuf[0] == SHELL_CMD_TAB) + { + tabcurr->pNext = (struct TabEntry*) malloc(sizeof(struct TabEntry)); + if(tabcurr->pNext != NULL) + { + memset(tabcurr->pNext, 0, sizeof(struct TabEntry)); + tabcurr->pNext->name = strdup((char *) (linebuf+1)); + if(tabcurr->pNext->name == NULL) + { + free(tabcurr->pNext); + tabcurr->pNext = NULL; + } + else + { + tabcurr = tabcurr->pNext; + } + } + } + + pos = 0; + } + } + else + { + if(pos > 0) + { + linebuf[pos-1] = *curr; + pos++; + } + } + + curr++; + } + + return ret; +} + +struct TabEntry* read_tab_completion(void) +{ + fd_set readset; + fd_set readsave; + struct timeval tv; + struct TabEntry head; + + if(g_context.sock < 0) + { + return NULL; + } + memset(&head, 0, sizeof(head)); + + FD_ZERO(&readsave); + FD_SET(g_context.sock, &readsave); + + while(1) + { + int ret; + + tv.tv_sec = 2; + tv.tv_usec = 0; + readset = readsave; + ret = select(FD_SETSIZE, &readset, NULL, NULL, &tv); + if(ret < 0) + { + if(errno == EINTR) + { + continue; + } + + perror("select"); + break; + } + else if(ret == 0) + { + /* Timeout */ + break; + } + else + { + if(FD_ISSET(g_context.sock, &readset)) + { + /* Do read */ + if(read_tab(g_context.sock, &head) < 0) + { + break; + } + } + } + } + + return head.pNext; +} + +int shell(void) +{ + fd_set readset; + FD_ZERO(&g_context.readsave); + + if(g_verbose) + { + fprintf(stderr, "Opening connection to %s port %d\n", g_context.args.ip, g_context.args.port); + } + + if((g_context.sock = connect_to(g_context.args.ip, g_context.args.port)) < 0) + { + return 1; + } + + if(g_context.args.notty == 0) + { + if((g_context.outsock = connect_to(g_context.args.ip, g_context.args.port+2)) < 0) + { + fprintf(stderr, "Could not connect to stdout channel\n"); + } + if((g_context.errsock = connect_to(g_context.args.ip, g_context.args.port+3)) < 0) + { + fprintf(stderr, "Could not connect to stderr channel\n"); + } + } + + /* + if((g_context.fssock == connect_to(g_context.args.ip, g_context.args.port+8)) < 0) + { + fprintf(stderr, "Could not connect to fs admin channel\n"); + } + */ + + if(!g_context.args.script) + { + init_readline(); + read_history(g_context.history_file); + history_set_pos(history_length); + + FD_SET(STDIN_FILENO, &g_context.readsave); + } + + /* Change to the current directory, should return our path */ + execute_line("cd ."); + + while(!g_context.exit) + { + int ret; + + readset = g_context.readsave; + ret = select(FD_SETSIZE, &readset, NULL, NULL, NULL); + if(ret < 0) + { + if(errno == EINTR) + { + continue; + } + + perror("select"); + break; + } + else if(ret == 0) + { + continue; + } + else + { + if(!g_context.args.script) + { + if(FD_ISSET(STDIN_FILENO, &readset)) + { + rl_callback_read_char(); + } + } + + if(FD_ISSET(g_context.sock, &readset)) + { + /* Do read */ + if(read_socket(g_context.sock) < 0) + { + close(g_context.sock); + g_context.sock = -1; + break; + } + } + + if((g_context.outsock >= 0) && FD_ISSET(g_context.outsock, &readset)) + { + if(read_outsocket(g_context.outsock) < 0) + { + FD_CLR(g_context.outsock, &g_context.readsave); + close(g_context.outsock); + g_context.outsock = -1; + } + } + if((g_context.errsock >= 0) && FD_ISSET(g_context.errsock, &readset)) + { + if(read_errsocket(g_context.errsock) < 0) + { + FD_CLR(g_context.errsock, &g_context.readsave); + close(g_context.errsock); + g_context.errsock = -1; + } + } + + if((g_context.fssock >= 0) && FD_ISSET(g_context.fssock, &readset)) + { + if(read_fssocket(g_context.fssock) < 0) + { + FD_CLR(g_context.fssock, &g_context.readsave); + close(g_context.fssock); + g_context.fssock = -1; + } + } + + } + } + + if(!g_context.args.script) + { + write_history(g_context.history_file); + rl_callback_handler_remove(); + } + + return 0; +} + +void shutdown_app(void) +{ + if(g_context.sock >= 0) + { + close(g_context.sock); + g_context.sock = -1; + } + if(g_context.outsock >= 0) + { + close(g_context.outsock); + g_context.outsock = -1; + } + if(g_context.errsock >= 0) + { + close(g_context.errsock); + g_context.errsock = -1; + } + if(g_context.fssock >= 0) + { + close(g_context.fssock); + g_context.fssock = -1; + } + + if(!g_context.args.script) + { + rl_callback_handler_remove(); + } +} + +void sig_call(int sig) +{ + if((sig == SIGINT) || (sig == SIGTERM)) + { + fprintf(stderr, "Exiting\n"); + + shutdown_app(); + exit(1); + } +} + +void build_histfile(void) +{ + if(g_context.args.hist == NULL) + { + char *home; + + home = getenv("HOME"); + if(home == NULL) + { + snprintf(g_context.history_file, PATH_MAX, "%s", HISTORY_FILE); + } + else + { + snprintf(g_context.history_file, PATH_MAX, "%s/%s", home, HISTORY_FILE); + } + } + else + { + snprintf(g_context.history_file, PATH_MAX, "%s", g_context.args.hist); + } +} + +int main(int argc, char **argv) +{ + int ret = 1; + + memset(&g_context, 0, sizeof(g_context)); + g_context.sock = -1; + g_context.outsock = -1; + g_context.errsock = -1; + g_context.fssock = -1; + g_context.fstdout = stdout; + g_context.fstderr = stderr; + if(parse_args(argc, argv, &g_context.args)) + { + build_histfile(); + if(shell() == 0) + { + ret = g_context.lasterr; + } + + shutdown_app(); + } + else + { + print_help(); + } + + return ret; +} diff --git a/pspsh/pspsh.o b/pspsh/pspsh.o new file mode 100644 index 0000000..87c01f6 Binary files /dev/null and b/pspsh/pspsh.o differ diff --git a/scripts/README b/scripts/README new file mode 100644 index 0000000..574d708 --- /dev/null +++ b/scripts/README @@ -0,0 +1 @@ +Some sample scripts for psplink to do a few things diff --git a/scripts/loadvsh.sh b/scripts/loadvsh.sh new file mode 100644 index 0000000..038203a --- /dev/null +++ b/scripts/loadvsh.sh @@ -0,0 +1,5 @@ +#!/usr/local/pspdev/bin/pspsh + +# Reload vsh maintaining psplink +reset vsh +flash0:/vsh/module/vshmain.prx diff --git a/sersh/Makefile b/sersh/Makefile new file mode 100644 index 0000000..54394b1 --- /dev/null +++ b/sersh/Makefile @@ -0,0 +1,13 @@ +OUTPUT=sersh +OBJS=sersh.o + +CFLAGS=-Wall -g +LIBS= + +all: sersh + +$(OUTPUT): $(OBJS) + $(CC) -o $@ $^ $(LIBS) + +clean: + rm -f $(OUTPUT) *.o diff --git a/sersh/sersh.c b/sersh/sersh.c new file mode 100644 index 0000000..f4b5ad9 --- /dev/null +++ b/sersh/sersh.c @@ -0,0 +1,273 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * sersh.c - PSPLINK pc serial shell + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/sersh/sersh.c $ + * $Id: sersh.c 2163 2007-02-01 21:25:53Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_SERIAL "/dev/ttyS0" +#define BAUD_RATE 115200 + +struct Args +{ + const char *ip; + unsigned int baud; + unsigned int realbaud; +}; + +struct GlobalContext +{ + struct Args args; + int exit; + fd_set readsave; + fd_set writesave; + int sock; +}; + +struct GlobalContext g_context; + +speed_t map_int_to_speed(int baud) +{ + speed_t ret = 0; + + switch(baud) + { + case 4800: ret = B4800; + break; + case 9600: ret = B9600; + break; + case 19200: ret = B19200; + break; + case 38400: ret = B38400; + break; + case 57600: ret = B57600; + break; + case 115200: ret = B115200; + break; + default: fprintf(stderr, "Unsupport baud rate %d\n", baud); + break; + }; + + return ret; +} + +int parse_args(int argc, char **argv, struct Args *args) +{ + memset(args, 0, sizeof(*args)); + args->realbaud = BAUD_RATE; + args->baud = map_int_to_speed(BAUD_RATE); + + while(1) + { + int ch; + int error = 0; + + ch = getopt(argc, argv, "b:"); + if(ch < 0) + { + break; + } + + switch(ch) + { + case 'b': args->baud = map_int_to_speed(atoi(optarg)); + args->realbaud = atoi(optarg); + if(args->baud == 0) + { + error = 1; + } + break; + default : error = 1; + break; + }; + + if(error) + { + return 0; + } + } + + argc -= optind; + argv += optind; + + if(argc < 1) + { + args->ip = DEFAULT_SERIAL; + } + else + { + args->ip = argv[0]; + } + + return 1; +} + +void print_help(void) +{ + fprintf(stderr, "SERSH Help\n"); + fprintf(stderr, "Usage: sersh [options] [device]\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "-b baud : Specify the baud rate (default %d)\n", BAUD_RATE); +} + +int read_socket(int sock) +{ + char buf[1024]; + int len; + + len = read(sock, buf, sizeof(buf)-1); + if(len < 0) + { + perror("read"); + return -1; + } + + /* EOF */ + if(len == 0) + { + return -1; + } + + buf[len] = 0; + + printf("%s", buf); + fflush(stdout); + + return len; +} + +int on_idle(void) +{ + struct termios options; + + g_context.sock = open(g_context.args.ip, O_RDWR | O_NOCTTY | O_NDELAY); + if(g_context.sock == -1) + { + perror("Unable to open serial port - "); + return 0; + } + else + { + fcntl(g_context.sock, F_SETFL, 0); + } + + tcgetattr(g_context.sock, &options); + cfsetispeed(&options, g_context.args.baud); + cfsetospeed(&options, g_context.args.baud); + options.c_cflag &= ~PARENB; + options.c_cflag &= ~CSTOPB; + options.c_cflag &= ~CSIZE; + options.c_cflag |= CS8; + options.c_cflag &= ~CRTSCTS; + options.c_cflag |= (CLOCAL | CREAD); + + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + options.c_iflag &= ~(IXON | IXOFF | IXANY); + options.c_iflag |= IGNCR; + + options.c_oflag &= ~OPOST; + + tcsetattr(g_context.sock, TCSANOW, &options); + FD_SET(g_context.sock, &g_context.readsave); + write(g_context.sock, "\n", 1); + + return 1; +} + +void shell(void) +{ + fd_set readset; + + printf("Opening %s baud %d\n", g_context.args.ip, g_context.args.realbaud); + + FD_ZERO(&g_context.readsave); + if(!on_idle()) + { + return; + } + + while(!g_context.exit) + { + int ret; + + readset = g_context.readsave; + ret = select(FD_SETSIZE, &readset, NULL, NULL, NULL); + if(ret < 0) + { + if(errno == EINTR) + { + continue; + } + + perror("select"); + break; + } + else if(ret == 0) + { + continue; + } + else + { + if(FD_ISSET(g_context.sock, &readset)) + { + /* Do read */ + if(read_socket(g_context.sock) < 0) + { + close(g_context.sock); + g_context.sock = -1; + g_context.exit = 1; + } + } + } + } +} + +void sig_call(int sig) +{ + if((sig == SIGINT) || (sig == SIGTERM)) + { + printf("Exiting\n"); + if(g_context.sock >= 0) + { + close(g_context.sock); + g_context.sock = -1; + } + exit(0); + } +} + +int main(int argc, char **argv) +{ + memset(&g_context, 0, sizeof(g_context)); + g_context.sock = -1; + if(parse_args(argc, argv, &g_context.args)) + { + shell(); + if(g_context.sock >= 0) + { + close(g_context.sock); + } + } + else + { + print_help(); + } + + return 0; +} diff --git a/tools/README b/tools/README new file mode 100644 index 0000000..fbf7e82 --- /dev/null +++ b/tools/README @@ -0,0 +1,2 @@ +This directory contains extra tools which are not specifically part of PSPLINK +but I am adding them here for people to play with. diff --git a/tools/debugmenu/Makefile b/tools/debugmenu/Makefile new file mode 100644 index 0000000..be91ff9 --- /dev/null +++ b/tools/debugmenu/Makefile @@ -0,0 +1,22 @@ +TARGET = debugmenu +OBJS = main.o exports.o + +# Use the kernel's small inbuilt libc +USE_KERNEL_LIBC = 1 +# Use only kernel libraries +USE_KERNEL_LIBS = 1 + +INCDIR = +LDFLAGS = -L../../libpsplink +CFLAGS = -Os -G0 -Wall -fno-builtin-printf -I../../usbhostfs -I../../psplink +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = +LIBS = -lpsplink + + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak + +LIBS += -lpspge_driver -lpsppower_driver diff --git a/tools/debugmenu/README b/tools/debugmenu/README new file mode 100644 index 0000000..d0ba4b8 --- /dev/null +++ b/tools/debugmenu/README @@ -0,0 +1,3 @@ +This is a simple tool which will show a menu when the L+R+START+SELECT are pressed. It allows you to currently +reset psplink, exit back to the vsh or power off the system. It also serves as an example of how to make +a simple Impose like menu for your psp. diff --git a/tools/debugmenu/exports.exp b/tools/debugmenu/exports.exp new file mode 100644 index 0000000..b7940e8 --- /dev/null +++ b/tools/debugmenu/exports.exp @@ -0,0 +1,12 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/tools/debugmenu/main.c b/tools/debugmenu/main.c new file mode 100644 index 0000000..e3dff78 --- /dev/null +++ b/tools/debugmenu/main.c @@ -0,0 +1,207 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK Debug/Impose menu + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/tools/debugmenu/main.c $ + * $Id: main.c 2018 2006-10-07 16:54:19Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PSP_MODULE_INFO("DebugMenu", PSP_MODULE_KERNEL, 1, 1); + +#define START_MENU 1 +static SceUID g_eventflag = -1; + +void psplinkReset(void); + +struct MenuOption +{ + const char *text; + void (*do_option)(void); +}; + +void menu_exit_to_vsh(void) +{ + sceKernelExitGame(); +} + +void menu_power_off(void) +{ + scePowerRequestStandby(); +} + +struct MenuOption options[] = { + { "Reset PSPLINK\n", psplinkReset }, + { "Exit to VSH\n", menu_exit_to_vsh }, + { "Power Off\n", menu_power_off }, +}; + +int opt_count = sizeof(options)/sizeof(struct MenuOption); + +#define TRIGGER (PSP_CTRL_LTRIGGER | PSP_CTRL_RTRIGGER | PSP_CTRL_START | PSP_CTRL_SELECT) + +void button_callback(int curr, int last, void *arg) +{ + if((curr & TRIGGER) == TRIGGER) + { + sceKernelSetEventFlag(g_eventflag, START_MENU); + } +} + +void redraw_menu(int selected) +{ + int i; + + pspDebugScreenSetXY(0, 0); + pspDebugScreenSetTextColor(0xFFFFFFFF); + pspDebugScreenPuts("PSPLINK Debug Menu\n\n\n"); + for(i = 0; i < opt_count; i++) + { + if(i == selected) + { + pspDebugScreenSetTextColor(0xFF00FF00); + pspDebugScreenPuts(options[i].text); + } + else + { + pspDebugScreenSetTextColor(0xFFFFFFFF); + pspDebugScreenPuts(options[i].text); + } + } +} + +void do_menu(void) +{ + SceCtrlData pad; + int selected = 0; + unsigned int lastbut = TRIGGER; + unsigned int curr = 0; + + redraw_menu(selected); + while(1) + { + sceCtrlPeekBufferPositive(&pad, 1); + + curr = pad.Buttons & ~lastbut; + + if(curr & PSP_CTRL_UP) + { + if(selected > 0) + { + selected--; + } + else if(selected == 0) + { + selected = opt_count-1; + } + redraw_menu(selected); + } + if(curr & PSP_CTRL_DOWN) + { + if(selected < (opt_count-1)) + { + selected++; + } + else if(selected == (opt_count-1)) + { + selected = 0; + } + redraw_menu(selected); + } + if(curr & PSP_CTRL_CIRCLE) + { + options[selected].do_option(); + } + else if(curr & PSP_CTRL_CROSS) + { + return; + } + lastbut = pad.Buttons; + if(sceDisplayWaitVblankStart() < 0) + { + sceKernelExitDeleteThread(0); + } + } +} + +int main_thread(SceSize args, void *argp) +{ + SceUID block_id; + void *vram; + + block_id = sceKernelAllocPartitionMemory(4, "debugmenu", PSP_SMEM_Low, 512*272*2, NULL); + if(block_id < 0) + { + Kprintf("Error could not allocate memory buffer 0x%08X\n", block_id); + goto error; + } + + vram = (void*) (0xA0000000 | (unsigned int) sceKernelGetBlockHeadAddr(block_id)); + g_eventflag = sceKernelCreateEventFlag("DebugMenuEvent", 0, 0, NULL); + if(g_eventflag < 0) + { + Kprintf("Could not create event flag %08X\n", g_eventflag); + goto error; + } + + //sceCtrlRegisterButtonCallback(0, PSP_CTRL_HOME, button_callback, NULL); + sceCtrlRegisterButtonCallback(3, TRIGGER, button_callback, NULL); + while(1) + { + unsigned int bits; + if(sceKernelWaitEventFlag(g_eventflag, START_MENU, + PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &bits, NULL) < 0) + { + break; + } + sceCtrlSetButtonMasks(0xFFFF, 1); // Mask lower 16bits + sceCtrlSetButtonMasks(0x10000, 2); // Always return HOME key + sceDisplaySetFrameBufferInternal(0, vram, 512, 0, 1); + pspDebugScreenInitEx(vram, 0, 0); + do_menu(); + sceDisplaySetFrameBufferInternal(0, 0, 512, 0, 1); + sceCtrlSetButtonMasks(0x10000, 0); // Unset HOME key + sceCtrlSetButtonMasks(0xFFFF, 0); // Unset mask + sceKernelClearEventFlag(g_eventflag, ~START_MENU); + } + +error: + sceKernelExitDeleteThread(0); + + return 0; +} + +/* Entry point */ +int module_start(SceSize args, void *argp) +{ + int thid; + + thid = sceKernelCreateThread("DebugMenu", main_thread, 15, 0x800, 0, NULL); + if(thid >= 0) + { + sceKernelStartThread(thid, args, argp); + } + return 0; +} + +/* Module stop entry */ +int module_stop(SceSize args, void *argp) +{ + return 0; +} diff --git a/tools/echo/Makefile b/tools/echo/Makefile new file mode 100644 index 0000000..ccea176 --- /dev/null +++ b/tools/echo/Makefile @@ -0,0 +1,17 @@ +TARGET = echo +OBJS = main.o + +BUILD_PRX=1 + +INCDIR = +CFLAGS = -O2 -G0 -Wall -I../../usbhostfs +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = +LDFLAGS = -L../../libusbhostfs + +LIBS = -lusbhostfs + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build.mak diff --git a/tools/echo/README b/tools/echo/README new file mode 100644 index 0000000..9cf020b --- /dev/null +++ b/tools/echo/README @@ -0,0 +1,7 @@ +This is a simple example of the Asynchronous Provider Framework (APF) for PSPLINK. +It creates an echo server so you can connect on to your localhost and it will +echo back the results to you. To prove it isn't cheating the text will also be +displayed on the PSP's screen. + +To use run echo.prx and it will print the port to connect to, now use something +like netcat to connect to localhost on the port specified and start typing. diff --git a/tools/echo/main.c b/tools/echo/main.c new file mode 100644 index 0000000..8629196 --- /dev/null +++ b/tools/echo/main.c @@ -0,0 +1,63 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - Basic example of the APF + * + * Copyright (c) 2006 James F + * + * $Id: main.c 1952 2006-06-25 15:57:15Z tyranid $ + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/tools/echo/main.c $ + */ +#include +#include +#include + +/* Define the module info section */ +PSP_MODULE_INFO("template", 0, 1, 1); + +/* Define the main thread's attribute value (optional) */ +PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER); + +struct AsyncEndpoint g_endp; + +int main(int argc, char *argv[]) +{ + int chan; + int running = 1; + + pspDebugScreenInit(); + chan = usbAsyncRegister(ASYNC_ALLOC_CHAN, &g_endp); + if(chan < 0) + { + printf("Could not allocate async channel\n"); + return 0; + } + + usbWaitForConnect(); + + printf("Allocated channel %d, connect to localhost port %d and start typing\n", chan, 10000 + chan); + while(running) + { + unsigned char buf[512]; + int len; + + len = usbAsyncRead(chan, buf, sizeof(buf)); + if(len < 0) + { + /* Error, most likely shutdown */ + break; + } + + if(len > 0) + { + pspDebugScreenPrintf("Read: %.*s\n", len, buf); + usbAsyncWrite(chan, buf, len); + } + } + + usbAsyncUnregister(chan); + + return 0; +} diff --git a/tools/reboothook/Makefile b/tools/reboothook/Makefile new file mode 100644 index 0000000..3de344f --- /dev/null +++ b/tools/reboothook/Makefile @@ -0,0 +1,12 @@ +all: reboothook.elf reboothook.bin + +CFLAGS = -Wl,-Ttext,0x883E0000 -nostartfiles + +%.elf: %.S + psp-gcc $(CFLAGS) -o $@ $^ + +%.bin: %.elf + psp-objcopy -O binary $^ $@ + +clean: + rm -f reboothook.bin reboothook.elf *.o diff --git a/tools/reboothook/README b/tools/reboothook/README new file mode 100644 index 0000000..e49ca44 --- /dev/null +++ b/tools/reboothook/README @@ -0,0 +1,21 @@ +Reboot hook for PSPLINK (c) TyRaNiD 2k6 + +This is a simple tool to patch out the plain text module check during a reset +of the kernel. It allows you to run pretty much anything from LoadExec such +as prx files which usually cause the system to drop back to the vsh. + +USAGE: + +Run make to build the executable and then in PSPLINK run setup.sh. Ensure +that the current shell directory is the same as the one containing +reboothook.bin. Once you run the script you can loadexec anything from ms0 +(host will not be up anyway at that point so it wont work). + +For example typing into the shell : + +./setup.sh +me @vsh ms0:/psp/game/psplink/psplink.prx + +will reboot the main part of psplink in vsh mode. Note if you do this you +can only reset if you either rerun the setup.sh or do +me ms0:/psp/game/psplink/eboot.pbp diff --git a/tools/reboothook/reboothook.S b/tools/reboothook/reboothook.S new file mode 100644 index 0000000..52c936a --- /dev/null +++ b/tools/reboothook/reboothook.S @@ -0,0 +1,33 @@ +# Reboot hooker, patches out the plain text prx check so you can loadexec +# any valid PSP module. +# NOTE: This is only for the v1.5 kernel +# (c) TyRaNiD 2k6 (inspired by umdemu) + + .set noreorder + .set noat + + .global _stage + + .global _start + .ent _start + +# _start is called instead of the reboot code +_start: +# Patch in a jump to _stage at the appropriate point in the reboot code + li $t1, 0x0a0f8005 + lui $t2, 0x88c0 + j 0x8C00000 + sw $t1, 0xFF8($t2) + + .end _start + + .ent _stage + +# Patch out the check in loadcore which refuses to load plain text modules +_stage: + li $t1, 0x88010000 + li $t2, 0x340D0001 + jr $v0 + sw $t2, 0x52e0($t1) + + .end _stage diff --git a/tools/reboothook/setup.sh b/tools/reboothook/setup.sh new file mode 100644 index 0000000..17c8780 --- /dev/null +++ b/tools/reboothook/setup.sh @@ -0,0 +1,7 @@ +# Load reboot hook into memory and patch the kernel to call it +# Must be run in the current directory of reboothook.bin +loadmem 0x883e0000 reboothook.bin +pokeh @sceLoadExec@+0x2384 0x883E +pokew 0x883e0080 0 +dcache wi +icache diff --git a/tools/remotejoy/Makefile b/tools/remotejoy/Makefile new file mode 100644 index 0000000..e5ade13 --- /dev/null +++ b/tools/remotejoy/Makefile @@ -0,0 +1,29 @@ +TARGET = remotejoy +OBJS = main.o kmode.o exports.o + +# Use the kernel's small inbuilt libc +USE_KERNEL_LIBC = 1 +# Use only kernel libraries +USE_KERNEL_LIBS = 1 + +INCDIR = +#CFLAGS = -O2 -G0 -Wall -fno-builtin-printf -DDEBUG +CFLAGS = -Os -G0 -Wall -fno-builtin-printf -I../../usbhostfs -I../../psplink +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) +LDFLAGS = -L../../libpsplink_driver -L../../libusbhostfs_driver +LIBS = -lpsppower_driver -lusbhostfs_driver -lpspdisplay_driver + +ifdef BUILD_PLUGIN +CFLAGS += -DBUILD_PLUGIN +#OBJS += libs.o apihook.o +LIBS += -lpspusb_driver +else +LIBS += -lpsplink_driver +endif + +LIBDIR = + + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak diff --git a/tools/remotejoy/README b/tools/remotejoy/README new file mode 100644 index 0000000..eb6b839 --- /dev/null +++ b/tools/remotejoy/README @@ -0,0 +1,39 @@ +RemoteJoy for PSPLINK v1.0 (c) TyRaNiD 2006 + +This is an example of the Asynchronous Provider Framework (APF) for PSPLINK, +it allows you to use a PC joypad connected to a Linux machine to control the +PSP as if you were using the real joypad. + +It is a demo of what could be done with the APF and is not really designed +to be worthwhile, however it could be useful for presentational work where +the PSP is fixed into a stand and you don't want to have to touch the PSP +itself. + +So how to use? + +First build the PC application, at this point you may want to create a mapfile +to ensure the buttons are mapped correctly and so you can use analog control +if your PC joypad supports it. Run the PC remotejoy application with the -b +switch passing in the name of the mapfile (i.e. -b mymap.txt). You also need to +supply the device filename of the joystick as the last paramter (this is usually +/dev/js0 or /dev/input/js0 depending on your version of linux). This starts a +simple configuration system which will ask you to either move a joystick or +press a button. If your pad does not support analogue input and you dont want +to map a digital pad to it when asked for the analogue input just press a button +instead. At this point you can edit the file and tweak the tol:num value if you +mapped the digital input to an analogue stick. Valid values are between 0 and +32767. + +Now run the remotejoy application with the name of the device file again but this +time specify the -m option passing in the name of the mapfile you created. Finally +run remotejoy.prx on the PSP and then load your game/application or even the VSH +and test it out. + +Note that this is not persistent, you need to load it up before you want to use it +and it does not support either use of the latch functions (which may affect some +games possibly I do not know) or any of the special keys (such as home/note etc). + +I have provided a mapping file for my MS Sidewinder USB gamepad so if you have that +you don't even need to make your own (unless you want to of course :P) + +Anyway have fun. diff --git a/tools/remotejoy/exports.exp b/tools/remotejoy/exports.exp new file mode 100644 index 0000000..b7940e8 --- /dev/null +++ b/tools/remotejoy/exports.exp @@ -0,0 +1,12 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/tools/remotejoy/kmode.S b/tools/remotejoy/kmode.S new file mode 100644 index 0000000..7426760 --- /dev/null +++ b/tools/remotejoy/kmode.S @@ -0,0 +1,92 @@ + + .set noreorder + .set noat + + .global psplinkSetK1 + .ent psplinkSetK1 + +psplinkSetK1: + move $v0, $k1 + jr $ra + move $k1, $a0 + + .end psplinkSetK1 + +# Try and do a fast copy with the vfpu + .global copy16to16 + .ent copy16to16 +copy16to16: + lui $a2, 0x4000 + or $a1, $a1, $a2 + + li $t1, 272 +1: + +# 480 * 2 / 64 + li $t0, (480/32) + +2: + lv.q C000, 0($a0) + lv.q C010, 16($a0) + lv.q C020, 32($a0) + lv.q C030, 48($a0) + addiu $t0, $t0, -1 + sv.q C000, 0($a1), wb + sv.q C010, 16($a1), wb + sv.q C020, 32($a1), wb + sv.q C030, 48($a1), wb + vnop + addiu $a0, $a0, 64 + bnez $t0, 2b + addiu $a1, $a1, 64 + + addiu $t1, $t1, -1 + bnez $t1, 1b + addiu $a0, $a0, (32*2) + + jr $ra + nop + + .end copy16to16 + +# Copy 32bit colour buffer to 16bit colour +# void copy32to16(unsigned int *in, unsigned short *out) + .global copy32to16 + .ent copy32to16 + +copy32to16: +# Each line is 480 * 4 / 32 units +# Need to increment 32 * 4 after + +# lui $a2, 0x4000 +# or $a1, $a1, $a2 + + li $t1, 272 +1: + li $t0, (480/16) + +2: + lv.q C000, 0($a0) + lv.q C010, 16($a0) + lv.q C020, 32($a0) + lv.q C030, 48($a0) + vt5650.q C100, C000 + vt5650.q C102, C010 + vt5650.q C110, C020 + vt5650.q C112, C030 + sv.q C100, 0($a1) + sv.q C110, 16($a1) + addiu $a0, $a0, 64 + addiu $t0, $t0, -1 + bnez $t0, 2b + addiu $a1, $a1, 32 + + addiu $t1, $t1, -1 + bnez $t1, 1b + addiu $a0, $a0, (32*4) + + jr $ra + nop + + .end copy32to16 + diff --git a/tools/remotejoy/main.c b/tools/remotejoy/main.c new file mode 100644 index 0000000..6919b4a --- /dev/null +++ b/tools/remotejoy/main.c @@ -0,0 +1,636 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK USB Remote Joystick Driver + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/tools/remotejoy/main.c $ + * $Id: main.c 2204 2007-03-11 19:16:01Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "remotejoy.h" + +PSP_MODULE_INFO("RemoteJoy", PSP_MODULE_KERNEL, 1, 1); + +//#define ENABLE_TIMING + +#ifdef BUILD_PLUGIN +#define HOSTFSDRIVER_NAME "USBHostFSDriver" +#define HOSTFSDRIVER_PID (0x1C9) +#include +#endif + +#define SCREEN_WAIT_TIMEOUT (100*1000) + +//#define DEBUG_PRINTF(x, ...) printf(x, ## __VA_ARGS__) +#define DEBUG_PRINTF(x, ...) + +SceCtrlData g_currjoy; +struct AsyncEndpoint g_endp; +SceUID g_scrthid = -1; +SceUID g_scrsema = -1; +SceUID g_screvent = -1; +char *g_scrptr = NULL; +int g_enabled = 0; +int g_droprate = 0; +int g_halfsize = 0; +int g_fullcolour = 0; +unsigned int g_lastframe = 0; + +int scePowerVolatileMemLock(int, char**, int*); +unsigned int psplinkSetK1(unsigned int k1); +extern u32 sceKernelSetDdrMemoryProtection; +int (*g_ctrl_common)(SceCtrlData *, int count, int type); + +#define ASYNC_JOY ASYNC_USER +#define ABS(x) ((x) < 0 ? -x : x) + +#define GET_JUMP_TARGET(x) (0x80000000 | (((x) & 0x03FFFFFF) << 2)) + +int map_axis(int real, int new) +{ + int val1, val2; + + val1 = ((int) real) - 127; + val2 = ((int) new) - 127; + + if(ABS(val1) > ABS(val2)) + { + return real; + } + else + { + return new; + } +} + +unsigned int calc_mask(void) +{ + int i; + unsigned int mask = 0; + + for(i = 0; i < 32; i++) + { + if(sceCtrlGetButtonMask(1 << i) == 1) + { + mask |= (1 << i); + } + } + + return mask; +} + +void add_values(SceCtrlData *pad_data, int count, int neg) +{ + int i; + int intc; + unsigned int buttons; + int k1; + + intc = pspSdkDisableInterrupts(); + + buttons = g_currjoy.Buttons; + asm __volatile__ ( "move %0, $k1" : "=r"(k1) ); + if(k1) + { + buttons &= ~calc_mask(); + } + + for(i = 0; i < count; i++) + { + if(neg) + { + pad_data[i].Buttons &= ~buttons; + } + else + { + pad_data[i].Buttons |= buttons; + } + + pad_data[i].Lx = map_axis(pad_data[i].Lx, g_currjoy.Lx); + pad_data[i].Ly = map_axis(pad_data[i].Ly, g_currjoy.Ly); + } + pspSdkEnableInterrupts(intc); +} + +int ctrl_hook_func(SceCtrlData *pad_data, int count, int type) +{ + int ret; + + ret = g_ctrl_common(pad_data, count, type); + if(ret <= 0) + { + return ret; + } + + add_values(pad_data, ret, type & 1); + + return ret; +} + +int hook_ctrl_function(unsigned int* jump) +{ + unsigned int target; + unsigned int func; + int inst; + + target = GET_JUMP_TARGET(*jump); + inst = _lw(target+8); + if((inst & ~0x03FFFFFF) != 0x0C000000) + { + return 1; + } + + g_ctrl_common = (void*) GET_JUMP_TARGET(inst); + + func = (unsigned int) ctrl_hook_func; + func = (func & 0x0FFFFFFF) >> 2; + _sw(0x0C000000 | func, target+8); + + return 0; +} + +void copy16to16(void *in, void *out); +void copy32to16(void *in, void *out); + +int copy_32bpp_raw(void *topaddr) +{ + unsigned int *u; + unsigned int *frame_addr; + int y; + + u = (unsigned int*) (g_scrptr + sizeof(struct JoyScrHeader)); + frame_addr = topaddr; + for(y = 0; y < 272; y++) + { + memcpy(u, frame_addr, 480*4); + u += 480; + frame_addr += 512; + } + + return 480*272*4; +} + +int copy_32bpp_vfpu(void *topaddr) +{ + struct JoyScrHeader *head = (struct JoyScrHeader*) (0x40000000 | (u32) g_scrptr); + + sceKernelDcacheWritebackInvalidateRange(g_scrptr, sizeof(struct JoyScrHeader)); + copy32to16(topaddr, (g_scrptr + sizeof(struct JoyScrHeader))); + + head->mode = 0; + return 480*272*2; +} + +int copy_16bpp_raw(void *topaddr) +{ + unsigned short *u; + unsigned short *frame_addr; + int y; + + u = (unsigned short*) (g_scrptr + sizeof(struct JoyScrHeader)); + frame_addr = topaddr; + for(y = 0; y < 272; y++) + { + memcpy(u, frame_addr, 480*2); + u += 480; + frame_addr += 512; + } + + return 480*272*2; +} + +int copy_16bpp_vfpu(void *topaddr) +{ + sceKernelDcacheWritebackInvalidateRange(g_scrptr, sizeof(struct JoyScrHeader)); + copy16to16(topaddr, g_scrptr + sizeof(struct JoyScrHeader)); + + return 480*272*2; +} + +int (*copy_32bpp)(void *topaddr) = copy_32bpp_vfpu; +int (*copy_16bpp)(void *topaddr) = copy_16bpp_vfpu; + +void reduce_16bpp(u16 *addr) +{ + int x, y; + u16 *addrout = addr; + + for(y = 272; y > 0; y -= 2) + { + for(x = 480; x > 0; x -= 2) + { + *addrout++ = *addr; + addr += 2; + } + addr += 480; + } +} + +void reduce_32bpp(u32 *addr) +{ + int x, y; + u32 *addrout = addr; + + for(y = 272; y > 0; y -= 2) + { + for(x = 480; x > 0; x -= 2) + { + *addrout++ = *addr; + addr += 2; + } + addr += 480; + } +} + +void set_frame_buf(void *topaddr, int bufferwidth, int pixelformat, int sync) +{ + unsigned int k1; + + k1 = psplinkSetK1(0); + if(g_lastframe == 0) + { + if((topaddr) && (sceKernelPollSema(g_scrsema, 1) == 0)) + { + /* We dont wait for this to complete, probably stupid ;) */ + sceKernelSetEventFlag(g_screvent, 1); + g_lastframe = g_droprate; + } + } + else + { + g_lastframe--; + } + psplinkSetK1(k1); + + sceDisplaySetFrameBufferInternal(2, topaddr, bufferwidth, pixelformat, sync); +} + +inline int build_frame(void) +{ + struct JoyScrHeader *head; + void *topaddr; + int bufferwidth; + int pixelformat; + int sync; + + /* Get the top level frame buffer, else get the normal frame buffer */ + sceDisplayGetFrameBufferInternal(0, &topaddr, &bufferwidth, &pixelformat, &sync); + if(topaddr == NULL) + { + sceDisplayGetFrameBufferInternal(2, &topaddr, &bufferwidth, &pixelformat, &sync); + } + + if(topaddr) + { + head = (struct JoyScrHeader*) g_scrptr; + head->magic = JOY_MAGIC; + head->mode = pixelformat; + head->ref = sceDisplayGetVcount(); + + switch(pixelformat) + { + case 3: head->size = copy_32bpp(topaddr); + break; + case 0: + case 1: + case 2: head->size = copy_16bpp(topaddr); + break; + default: head->size = 0; + break; + }; + + if(head->size > 0) + { + if(g_halfsize) + { + if(g_fullcolour && (pixelformat == 3)) + { + reduce_32bpp((u32*) (g_scrptr + sizeof(struct JoyScrHeader))); + } + else + { + reduce_16bpp((u16*) (g_scrptr + sizeof(struct JoyScrHeader))); + } + head->size >>= 2; + } + return 1; + } + } + + return 0; +} + +int screen_thread(SceSize args, void *argp) +{ + struct JoyScrHeader *head; + int size; + u32* p; + u32 baseaddr; + u32 func; + + /* Should actually hook the memory correctly :/ */ + + /* Enable free memory */ + _sw(0xFFFFFFFF, 0xBC00000C); + g_scrptr = (char*) (0x08800000-(512*1024)); + + g_scrsema = sceKernelCreateSema("ScreenSema", 0, 1, 1, NULL); + if(g_scrsema < 0) + { + DEBUG_PRINTF("Could not create sema 0x%08X\n", g_scrsema); + sceKernelExitDeleteThread(0); + } + + if(sceKernelDevkitVersion() >= 0x01050001) + { + p = &sceKernelSetDdrMemoryProtection; + + baseaddr = GET_JUMP_TARGET(*p); + _sw(0x03E00008, baseaddr); + _sw(0x00001021, baseaddr+4); + sceKernelDcacheWritebackInvalidateRange((void*) baseaddr, 8); + sceKernelIcacheInvalidateRange((void*) baseaddr, 8); + } + + p = (u32*) sceDisplaySetFrameBuf; + + baseaddr = GET_JUMP_TARGET(*p); + func = (unsigned int) set_frame_buf; + func = (func & 0x0FFFFFFF) >> 2; + _sw(0x08000000 | func, baseaddr); + _sw(0, baseaddr+4); + + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + + /* Display current frame */ + + if(build_frame()) + { + head = (struct JoyScrHeader*) g_scrptr; + size = head->size; + + usbWriteBulkData(ASYNC_JOY, g_scrptr, sizeof(struct JoyScrHeader) + size); + } + + while(1) + { + int ret; + unsigned int status; + SceUInt timeout; + + timeout = SCREEN_WAIT_TIMEOUT; + ret = sceKernelWaitEventFlag(g_screvent, 3, PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &status, &timeout); + if((ret < 0) && (ret != SCE_KERNEL_ERROR_WAIT_TIMEOUT)) + { + sceKernelExitDeleteThread(0); + } + + if((status & 1) || (ret == SCE_KERNEL_ERROR_WAIT_TIMEOUT)) + { +#ifdef ENABLE_TIMING + unsigned int fstart, fmid, fend; + + asm __volatile__ ( "mfc0 %0, $9\n" : "=r"(fstart) ); +#endif + _sw(0xFFFFFFFF, 0xBC00000C); + if(build_frame()) + { +#ifdef ENABLE_TIMING + asm __volatile__ ( "mfc0 %0, $9\n" : "=r"(fmid) ); +#endif + head = (struct JoyScrHeader*) g_scrptr; + size = head->size; + + usbWriteBulkData(ASYNC_JOY, g_scrptr, sizeof(struct JoyScrHeader) + size); +#ifdef ENABLE_TIMING + asm __volatile__ ( "mfc0 %0, $9\n" : "=r"(fend) ); + DEBUG_PRINTF("Total: 0x%08X Frame: 0x%08X Usb: 0x%08X\n", fend - fstart, fmid-fstart, fend-fmid); +#endif + } + + if(ret != SCE_KERNEL_ERROR_WAIT_TIMEOUT) + { + sceKernelSignalSema(g_scrsema, 1); + } + } + + if(status & 2) + { + sceKernelSleepThread(); + } + } + + return 0; +} + +void do_screen_cmd(unsigned int value) +{ + if(value & SCREEN_CMD_FULLCOLOR) + { + copy_32bpp = copy_32bpp_raw; + g_fullcolour = 1; + } + else + { + copy_32bpp = copy_32bpp_vfpu; + g_fullcolour = 0; + } + + if(value & SCREEN_CMD_HSIZE) + { + g_halfsize = 1; + } + else + { + g_halfsize = 0; + } + + g_droprate = SCREEN_CMD_GETDROPRATE(value); + + if(value & SCREEN_CMD_ACTIVE) + { + /* Create a thread */ + if(g_scrthid < 0) + { + g_screvent = sceKernelCreateEventFlag("ScreenEvent", 0, 0, NULL); + if(g_screvent < 0) + { + DEBUG_PRINTF("Could not create event 0x%08X\n", g_screvent); + return; + } + + g_scrthid = sceKernelCreateThread("ScreenThread", screen_thread, 16, 0x800, PSP_THREAD_ATTR_VFPU, NULL); + if(g_scrthid >= 0) + { + sceKernelStartThread(g_scrthid, 0, NULL); + } + g_enabled = 1; + } + else + { + if(!g_enabled) + { + sceKernelWakeupThread(g_scrthid); + g_enabled = 1; + } + } + } + else + { + /* Disable the screen display */ + /* Stop the thread at the next available opportunity */ + if(g_scrthid >= 0) + { + if(g_enabled) + { + sceKernelSetEventFlag(g_screvent, 2); + g_enabled = 0; + } + } + } +} + +void send_screen_probe(void) +{ + struct JoyScrHeader head; + + head.magic = JOY_MAGIC; + head.mode = -1; + head.size = 0; + head.ref = 0; + + usbAsyncWrite(ASYNC_JOY, &head, sizeof(head)); +} + +int main_thread(SceSize args, void *argp) +{ + struct JoyEvent joyevent; + int intc; + +#ifdef BUILD_PLUGIN + int retVal = 0; + + retVal = sceUsbStart(PSP_USBBUS_DRIVERNAME, 0, 0); + if (retVal != 0) { + Kprintf("Error starting USB Bus driver (0x%08X)\n", retVal); + return 0; + } + retVal = sceUsbStart(HOSTFSDRIVER_NAME, 0, 0); + if (retVal != 0) { + Kprintf("Error starting USB Host driver (0x%08X)\n", + retVal); + return 0; + } + + retVal = sceUsbActivate(HOSTFSDRIVER_PID); +#endif + + if(hook_ctrl_function((unsigned int*) sceCtrlReadBufferPositive) || + hook_ctrl_function((unsigned int*) sceCtrlPeekBufferPositive) || + hook_ctrl_function((unsigned int*) sceCtrlReadBufferNegative) || + hook_ctrl_function((unsigned int*) sceCtrlPeekBufferNegative)) + { + DEBUG_PRINTF("Could not hook controller functions\n"); + sceKernelExitDeleteThread(0); + } + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + + if(usbAsyncRegister(ASYNC_JOY, &g_endp) < 0) + { + DEBUG_PRINTF("Could not register remotejoy provider\n"); + sceKernelExitDeleteThread(0); + } + + usbWaitForConnect(); + + /* Send a probe packet for screen display */ + send_screen_probe(); + + while(1) + { + int len; + len = usbAsyncRead(ASYNC_JOY, (void*) &joyevent, sizeof(joyevent)); + + if((len != sizeof(joyevent)) || (joyevent.magic != JOY_MAGIC)) + { + if(len < 0) + { + /* Delay thread, necessary to ensure that the kernel can reboot :) */ + sceKernelDelayThread(250000); + } + else + { + DEBUG_PRINTF("Invalid read size %d\n", len); + usbAsyncFlush(ASYNC_JOY); + } + continue; + } + + intc = pspSdkDisableInterrupts(); + switch(joyevent.type) + { + case TYPE_BUTTON_UP: g_currjoy.Buttons &= ~joyevent.value; + break; + case TYPE_BUTTON_DOWN: g_currjoy.Buttons |= joyevent.value; + break; + case TYPE_ANALOG_Y: g_currjoy.Ly = joyevent.value; + break; + case TYPE_ANALOG_X: g_currjoy.Lx = joyevent.value; + break; + default: break; + }; + pspSdkEnableInterrupts(intc); + + /* We do screen stuff outside the disabled interrupts */ + if(joyevent.type == TYPE_SCREEN_CMD) + { + do_screen_cmd(joyevent.value); + } + scePowerTick(0); + } + + return 0; +} + +/* Entry point */ +int module_start(SceSize args, void *argp) +{ + int thid; + + memset(&g_currjoy, 0, sizeof(g_currjoy)); + g_currjoy.Lx = 0x80; + g_currjoy.Ly = 0x80; + /* Create a high priority thread */ + thid = sceKernelCreateThread("RemoteJoy", main_thread, 15, 0x800, 0, NULL); + if(thid >= 0) + { + sceKernelStartThread(thid, args, argp); + } + return 0; +} + +/* Module stop entry */ +int module_stop(SceSize args, void *argp) +{ + return 0; +} diff --git a/tools/remotejoy/parse_dump.c b/tools/remotejoy/parse_dump.c new file mode 100644 index 0000000..8434490 --- /dev/null +++ b/tools/remotejoy/parse_dump.c @@ -0,0 +1,64 @@ +/* Simple tool to parse a binary dump of the video stream */ +#include +#include "remotejoy.h" + +int main(int argc, char **argv) +{ + struct JoyScrHeader head; + FILE *fp; + int frames = 0; + int start_frame = 0; + int end_frame = 0; + double fTotal = 0.0; + double fSeconds = 0.0; + + if(argc < 2) + { + printf("Usage: parse_dump file.bin\n"); + return 1; + } + + fp = fopen(argv[1], "rb"); + if(fp == NULL) + { + printf("Could not open file %s\n", argv[1]); + return 1; + } + + while(1) + { + if(fread(&head, sizeof(head), 1, fp) != 1) + { + break; + } + + if(head.magic != JOY_MAGIC) + { + printf("Invalid magic %08X\n", head.magic); + continue; + } + + if(head.size != 0) + { + if((start_frame == 0) && (frames != 0)) + { + start_frame = head.ref; + } + end_frame = head.ref; + printf("%d - size %d, ref %d\n", frames, head.size, head.ref); + + frames++; + fseek(fp, head.size, SEEK_CUR); + } + } + + fclose(fp); + + fTotal = (double)frames; + fSeconds = ((double)(end_frame-start_frame)) / 60.0; + + printf("Start: %d, end %d\n", start_frame, end_frame); + printf("Number of frames: %d, total vsyncs: %d , seconds %f, avg fps %f\n", frames, end_frame - start_frame, fSeconds, + fTotal/fSeconds); + +} diff --git a/tools/remotejoy/pc/Makefile b/tools/remotejoy/pc/Makefile new file mode 100644 index 0000000..e8dfaee --- /dev/null +++ b/tools/remotejoy/pc/Makefile @@ -0,0 +1,19 @@ +OUTPUT=remotejoy +OBJS=remotejoy.o + +CFLAGS=-Wall -g + +PREFIX=$(shell psp-config --pspdev-path 2> /dev/null) + +all: $(OUTPUT) + +$(OUTPUT): $(OBJS) + $(CC) -o $@ $^ + +install: $(OUTPUT) + @echo "Installing $(OUTPUT)..." + @if ( test $(PREFIX) ); then { mkdir -p $(PREFIX)/bin && cp $(OUTPUT) $(PREFIX)/bin; } else { echo "Error: psp-config not found!"; exit 1; } fi + @echo "Done!" + +clean: + rm -f $(OUTPUT) *.o diff --git a/tools/remotejoy/pc/remotejoy b/tools/remotejoy/pc/remotejoy new file mode 100755 index 0000000..16de92d Binary files /dev/null and b/tools/remotejoy/pc/remotejoy differ diff --git a/tools/remotejoy/pc/remotejoy.c b/tools/remotejoy/pc/remotejoy.c new file mode 100644 index 0000000..116eaa8 --- /dev/null +++ b/tools/remotejoy/pc/remotejoy.c @@ -0,0 +1,919 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * remotejoy.c - PSPLINK PC remote joystick handler + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/tools/remotejoy/pc/remotejoy.c $ + * $Id: remotejoy.c 2398 2008-06-07 18:57:39Z iwn $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../remotejoy.h" + +#define DEFAULT_PORT 10004 +#define DEFAULT_IP "localhost" + +#define MAX_AXES_NUM 32767 +#define DIGITAL_TOL 10000 + +#ifndef SOL_TCP + #define SOL_TCP IPPROTO_TCP +#endif + +#if defined BUILD_BIGENDIAN || defined _BIG_ENDIAN +uint16_t swap16(uint16_t i) +{ + uint8_t *p = (uint8_t *) &i; + uint16_t ret; + + ret = (p[1] << 8) | p[0]; + + return ret; +} + +uint32_t swap32(uint32_t i) +{ + uint8_t *p = (uint8_t *) &i; + uint32_t ret; + + ret = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; + + return ret; +} + +uint64_t swap64(uint64_t i) +{ + uint8_t *p = (uint8_t *) &i; + uint64_t ret; + + ret = (uint64_t) p[0] | ((uint64_t) p[1] << 8) | ((uint64_t) p[2] << 16) | ((uint64_t) p[3] << 24) + | ((uint64_t) p[4] << 32) | ((uint64_t) p[5] << 40) | ((uint64_t) p[6] << 48) | ((uint64_t) p[7] << 56); + + return ret; +} +#define LE16(x) swap16(x) +#define LE32(x) swap32(x) +#define LE64(x) swap64(x) +#else +#define LE16(x) (x) +#define LE32(x) (x) +#define LE64(x) (x) +#endif + +enum PspCtrlButtons +{ + /** Select button. */ + PSP_CTRL_SELECT = 0x000001, + /** Start button. */ + PSP_CTRL_START = 0x000008, + /** Up D-Pad button. */ + PSP_CTRL_UP = 0x000010, + /** Right D-Pad button. */ + PSP_CTRL_RIGHT = 0x000020, + /** Down D-Pad button. */ + PSP_CTRL_DOWN = 0x000040, + /** Left D-Pad button. */ + PSP_CTRL_LEFT = 0x000080, + /** Left trigger. */ + PSP_CTRL_LTRIGGER = 0x000100, + /** Right trigger. */ + PSP_CTRL_RTRIGGER = 0x000200, + /** Triangle button. */ + PSP_CTRL_TRIANGLE = 0x001000, + /** Circle button. */ + PSP_CTRL_CIRCLE = 0x002000, + /** Cross button. */ + PSP_CTRL_CROSS = 0x004000, + /** Square button. */ + PSP_CTRL_SQUARE = 0x008000, + /** Home button. */ + PSP_CTRL_HOME = 0x010000, + /** Hold button. */ + PSP_CTRL_HOLD = 0x020000, + /** Music Note button. */ + PSP_CTRL_NOTE = 0x800000, +}; + +enum PspButtons +{ + PSP_BUTTON_CROSS = 0, + PSP_BUTTON_CIRCLE = 1, + PSP_BUTTON_TRIANGLE = 2, + PSP_BUTTON_SQUARE = 3, + PSP_BUTTON_LTRIGGER = 4, + PSP_BUTTON_RTRIGGER = 5, + PSP_BUTTON_START = 6, + PSP_BUTTON_SELECT = 7, + PSP_BUTTON_UP = 8, + PSP_BUTTON_DOWN = 9, + PSP_BUTTON_LEFT = 10, + PSP_BUTTON_RIGHT = 11, +}; + +unsigned int g_bitmap[12] = { + PSP_CTRL_CROSS, PSP_CTRL_CIRCLE, PSP_CTRL_TRIANGLE, PSP_CTRL_SQUARE, + PSP_CTRL_LTRIGGER, PSP_CTRL_RTRIGGER, PSP_CTRL_START, PSP_CTRL_SELECT, + PSP_CTRL_UP, PSP_CTRL_DOWN, PSP_CTRL_LEFT, PSP_CTRL_RIGHT, +}; + +const char *map_names[8] = { + "cross", "circle", "triangle", "square", + "ltrig", "rtrig", "start", "select", +}; + +/* Maps the buttons on the joystick to the buttons on the PSP controller */ +unsigned int *g_buttmap = NULL; + +struct Args +{ + const char *ip; + unsigned short port; + const char *dev; + const char *mapfile; + const char *buildmap; + int verbose; + int daemon; +}; + +struct GlobalContext +{ + struct Args args; + struct sockaddr_in serv; + char name[128]; + unsigned int version; + unsigned char axes; + unsigned char buttons; + int exit; + int digital; + int analog; + int tol; +}; + +struct GlobalContext g_context; + +#define VERBOSE (g_context.args.verbose) + +int fixed_write(int s, const void *buf, int len) +{ + int written = 0; + + while(written < len) + { + int ret; + + ret = write(s, buf+written, len-written); + if(ret < 0) + { + if(errno != EINTR) + { + perror("write"); + written = -1; + break; + } + } + else + { + written += ret; + } + } + + return written; +} + +int parse_args(int argc, char **argv, struct Args *args) +{ + memset(args, 0, sizeof(*args)); + args->ip = DEFAULT_IP; + args->port = DEFAULT_PORT; + + while(1) + { + int ch; + int error = 0; + + ch = getopt(argc, argv, "vdp:i:m:b:"); + + if(ch < 0) + { + break; + } + + switch(ch) + { + case 'p': args->port = atoi(optarg); + break; + case 'i': args->ip = optarg; + break; + case 'm': args->mapfile = optarg; + break; + case 'b': args->buildmap = optarg; + break; + case 'd': args->daemon = 1; + break; + case 'v': args->verbose = 1; + break; + default : error = 1; + break; + }; + + if(error) + { + return 0; + } + } + + argc -= optind; + argv += optind; + + if(argc < 1) + { + return 0; + } + else + { + args->dev = argv[0]; + } + + return 1; +} + +void print_help(void) +{ + fprintf(stderr, "Remotejoy Help\n"); + fprintf(stderr, "Usage: remotejoy [options] device\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "-p port : Specify the port number\n"); + fprintf(stderr, "-i ip : Specify the ip address (default %s)\n", DEFAULT_IP); + fprintf(stderr, "-m mapfile : Specify a file to map joystick buttons to the PSP\n"); + fprintf(stderr, "-b mapfile : Build a map file\n"); + fprintf(stderr, "-v : Verbose mode\n"); +} + +int init_sockaddr(struct sockaddr_in *name, const char *ipaddr, unsigned short port) +{ + struct hostent *hostinfo; + + name->sin_family = AF_INET; + name->sin_port = htons(port); + hostinfo = gethostbyname(ipaddr); + if(hostinfo == NULL) + { + fprintf(stderr, "Unknown host %s\n", ipaddr); + return 0; + } + name->sin_addr = *(struct in_addr *) hostinfo->h_addr; + + return 1; +} + +int connect_to(const char *ipaddr, unsigned short port) +{ + struct sockaddr_in name; + int sock = -1; + int flag = 1; + + sock = socket(PF_INET, SOCK_STREAM, 0); + + if(sock < 0) + { + perror("socket"); + return -1; + } + + if(!init_sockaddr(&name, ipaddr, port)) + { + printf("Could not initialise socket address\n"); + close(sock); + return -1; + } + + if(connect(sock, (struct sockaddr *) &name, sizeof(name)) < 0) + { + perror("connect"); + close(sock); + return -1; + } + + /* Disable NAGLE's algorithm to prevent the packets being joined */ + setsockopt(sock, SOL_TCP, TCP_NODELAY, &flag, sizeof(int)); + + return sock; +} + +int get_joyinfo(int joy) +{ + if(ioctl(joy, JSIOCGNAME(sizeof(g_context.name)), g_context.name) < 0) + { + perror("ioctl"); + return 0; + } + + if(ioctl(joy, JSIOCGVERSION, &g_context.version) < 0) + { + perror("ioctl"); + return 0; + } + + if(ioctl(joy, JSIOCGAXES, &g_context.axes) < 0) + { + perror("ioctl"); + return 0; + } + + if(ioctl(joy, JSIOCGBUTTONS, &g_context.buttons) < 0) + { + perror("ioctl"); + return 0; + } + + return 1; +} + +void remove_wsp(char *buf) +{ + int len = strlen(buf); + int i = 0; + + while(isspace(buf[i])) + { + i++; + } + + if(i > 0) + { + len -= i; + memmove(buf, &buf[i], len + 1); + } + + if(len <= 0) + { + return; + } + + i = len-1; + while(isspace(buf[i])) + { + buf[i--] = 0; + } +} + +int build_map(const char *mapfile, int buttons) +{ + int i; + FILE *fp; + + g_context.analog = -1; + g_context.tol = DIGITAL_TOL; + + g_buttmap = (unsigned int *) malloc(buttons * sizeof(unsigned int)); + if(g_buttmap == NULL) + { + return 0; + } + + for(i = 0; i < buttons; i++) + { + /* Fill with mappings, repeat if more than 8 buttons */ + g_buttmap[i] = i % 8; + } + + if(mapfile) + { + char buffer[512]; + int line = 0; + + fp = fopen(mapfile, "r"); + if(fp == NULL) + { + fprintf(stderr, "Could not open mapfile %s\n", mapfile); + return 0; + } + + while(fgets(buffer, sizeof(buffer), fp)) + { + char *tok, *val; + int butt; + line++; + remove_wsp(buffer); + + if((buffer[0] == '#') || (buffer[0] == 0)) /* Comment or empty line */ + { + continue; + } + + tok = strtok(buffer, ":"); + val = strtok(NULL, ""); + if((tok == NULL) || (val == NULL)) + { + printf("Invalid mapping on line %d\n", line); + continue; + } + + butt = atoi(val); + for(i = 0; i < 8; i++) + { + if(strcasecmp(map_names[i], tok) == 0) + { + g_buttmap[butt] = i; + break; + } + } + + if(i == 8) + { + if(strcasecmp("analog", tok) == 0) + { + g_context.analog = butt; + } + else if(strcasecmp("digital", tok) == 0) + { + g_context.digital = butt; + } + else if(strcasecmp("tol", tok) == 0) + { + g_context.tol = atoi(val); + } + else + { + fprintf(stderr, "Unknown map type %s\n", tok); + } + } + } + fclose(fp); + } + + return 1; +} + +int read_event(int joy, int type, struct js_event *joydata) +{ + int len; + + while(1) + { + len = read(joy, joydata, sizeof(struct js_event)); + if(len < 0) + { + if(errno != EINTR) + { + perror("read"); + return 0; + } + else + { + continue; + } + } + + if(joydata->type & JS_EVENT_INIT) + { + continue; + } + + if(joydata->type & type) + { + break; + } + } + + return 1; +} + +void make_mapfile(void) +{ + int joy = -1; + struct js_event joydata; + FILE *fp = NULL; + int i; + + do + { + joy = open(g_context.args.dev, O_RDONLY); + if(joy < 0) + { + perror("open"); + break; + } + + if(!get_joyinfo(joy)) + { + break; + } + + if(VERBOSE) + { + printf("name: %s, axes: %d, buttons: %d\n", g_context.name, + g_context.axes, g_context.buttons); + } + + + fp = fopen(g_context.args.buildmap, "w"); + if(fp == NULL) + { + fprintf(stderr, "Could not open file %s\n", g_context.args.buildmap); + break; + } + + fprintf(fp, "tol:%d\n", DIGITAL_TOL); + + printf("Move the stick you want for digital input (can use an analogue stick)\n"); + do + { + if(!read_event(joy, JS_EVENT_AXIS, &joydata)) + { + break; + } + } + while(abs(joydata.value) < DIGITAL_TOL); + + fprintf(fp, "digital:%d\n", joydata.number / 2); + for(i = 0; i < 8; i++) + { + fprintf(stderr, "Press button to map to %s\n", map_names[i]); + while(1) + { + if(!read_event(joy, JS_EVENT_BUTTON, &joydata)) + { + goto error; + } + + if(joydata.value) + { + break; + } + } + fprintf(fp, "%s:%d\n", map_names[i], joydata.number); + } + + printf("Move the stick you want for analog input (or press a button to disable)\n"); + while(1) + { + if(!read_event(joy, JS_EVENT_AXIS | JS_EVENT_BUTTON, &joydata)) + { + break; + } + + if(joydata.type == JS_EVENT_AXIS) + { + if(abs(joydata.value) < DIGITAL_TOL) + { + continue; + } + + fprintf(fp, "analog:%d\n", joydata.number / 2); + break; + } + else if((joydata.type == JS_EVENT_BUTTON) && (joydata.value)) + { + break; + } + } + } + while(0); + +error: + + if(joy >= 0) + { + close(joy); + } + + if(fp != NULL) + { + fclose(fp); + } +} + +int send_event(int sock, int type, unsigned int value) +{ + struct JoyEvent event; + + if(sock < 0) + { + return 0; + } + + /* Note, should swap endian */ + event.magic = LE32(JOY_MAGIC); + event.type = LE32(type); + event.value = LE32(value); + + if(fixed_write(sock, &event, sizeof(event)) != sizeof(event)) + { + fprintf(stderr, "Could not write out data to socket\n"); + return 0; + } + + return 1; +} + +void mainloop(void) +{ + int joy = -1; + int sock = -1; + struct js_event joydata; + unsigned int button_state = 0; + + do + { + joy = open(g_context.args.dev, O_RDONLY); + if(joy < 0) + { + perror("open"); + break; + } + + if(!get_joyinfo(joy)) + { + break; + } + + if(VERBOSE) + { + printf("name: %s, axes: %d, buttons: %d\n", g_context.name, + g_context.axes, g_context.buttons); + } + + if(!build_map(g_context.args.mapfile, g_context.buttons)) + { + break; + } + + sock = connect_to(g_context.args.ip, g_context.args.port); + if(sock < 0) + { + break; + } + + while(!g_context.exit) + { + int len; + len = read(joy, &joydata, sizeof(joydata)); + if(len < 0) + { + if(errno != EINTR) + { + perror("read"); + break; + } + else + { + continue; + } + } + + joydata.type &= ~JS_EVENT_INIT; + + if(joydata.type == JS_EVENT_BUTTON) + { + unsigned int bitmap = g_bitmap[g_buttmap[joydata.number]]; + if(joydata.value) + { + button_state |= bitmap; + if(!send_event(sock, TYPE_BUTTON_DOWN, bitmap)) + { + break; + } + } + else + { + button_state &= ~bitmap; + if(!send_event(sock, TYPE_BUTTON_UP, bitmap)) + { + break; + } + } + + if(VERBOSE) + { + printf("Button %d event\n", joydata.number); + printf("Button state: %08X\n", button_state); + } + } + else if(joydata.type == JS_EVENT_AXIS) + { + unsigned int curr_butt = 0; + + if(VERBOSE) + { + printf("Axis %d event\n", joydata.number/2); + } + + if(g_context.digital == (joydata.number/2)) + { + if(joydata.number & 1) + { + if(joydata.value > g_context.tol) + { + curr_butt = PSP_CTRL_DOWN; + } + else if(joydata.value < -g_context.tol) + { + curr_butt = PSP_CTRL_UP; + } + else + { + /* Do nothing */ + } + + if((button_state & PSP_CTRL_UP) && (curr_butt != PSP_CTRL_UP)) + { + /* Send UP up */ + button_state &= ~PSP_CTRL_UP; + if(!send_event(sock, TYPE_BUTTON_UP, PSP_CTRL_UP)) + { + break; + } + } + + if((button_state & PSP_CTRL_DOWN) && (curr_butt != PSP_CTRL_DOWN)) + { + /* Send DOWN up */ + button_state &= ~PSP_CTRL_DOWN; + if(!send_event(sock, TYPE_BUTTON_UP, PSP_CTRL_DOWN)) + { + break; + } + } + + if(((button_state & curr_butt) == 0) && (curr_butt != 0)) + { + /* Send down */ + button_state |= curr_butt; + if(!send_event(sock, TYPE_BUTTON_DOWN, curr_butt)) + { + break; + } + } + } + else + { + if(joydata.value > g_context.tol) + { + curr_butt = PSP_CTRL_RIGHT; + } + else if(joydata.value < -g_context.tol) + { + curr_butt = PSP_CTRL_LEFT; + } + else + { + /* Do nothing */ + } + + if((button_state & PSP_CTRL_RIGHT) && (curr_butt != PSP_CTRL_RIGHT)) + { + /* Send right up */ + button_state &= ~PSP_CTRL_RIGHT; + if(!send_event(sock, TYPE_BUTTON_UP, PSP_CTRL_RIGHT)) + { + break; + } + } + + if((button_state & PSP_CTRL_LEFT) && (curr_butt != PSP_CTRL_LEFT)) + { + /* Send left up */ + button_state &= ~PSP_CTRL_LEFT; + if(!send_event(sock, TYPE_BUTTON_UP, PSP_CTRL_LEFT)) + { + break; + } + } + + if(((button_state & curr_butt) == 0) && (curr_butt != 0)) + { + /* Send down */ + button_state |= curr_butt; + if(!send_event(sock, TYPE_BUTTON_DOWN, curr_butt)) + { + break; + } + } + } + } + + if(g_context.analog == (joydata.number / 2)) + { + int val = joydata.value + MAX_AXES_NUM; + if(joydata.number & 1) + { + /* Send Ly (val * 255) / (MAX_AXES_NUM * 2)) */ + if(!send_event(sock, TYPE_ANALOG_Y, (val * 255) / (MAX_AXES_NUM * 2))) + { + break; + } + } + else + { + /* Send Lx (val * 255) / (MAX_AXES_NUM * 2)) */ + if(!send_event(sock, TYPE_ANALOG_X, (val * 255) / (MAX_AXES_NUM * 2))) + { + break; + } + } + } + } + + } + } + while(0); + + if(joy >= 0) + { + close(joy); + } + + if(sock >= 0) + { + close(sock); + } + + if(g_buttmap) + { + free(g_buttmap); + } + + printf("Exiting\n"); +} + +void signal_handler(int sig) +{ + g_context.exit = 1; +} + +void setup_signals(void) +{ + struct sigaction act; + + memset(&act, 0, sizeof(act)); + act.sa_handler = signal_handler; + + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); +} + +int main(int argc, char **argv) +{ + printf("Remote Joy for PSP (c) TyRaNiD 2k6\n"); + memset(&g_context, 0, sizeof(g_context)); + if(parse_args(argc, argv, &g_context.args)) + { + if(g_context.args.buildmap) + { + make_mapfile(); + } + else + { + if(g_context.args.daemon) + { + pid_t pid = fork(); + + if(pid > 0) + { + /* Parent, just exit */ + return 0; + } + else if(pid < 0) + { + /* Error, print and exit */ + perror("fork:"); + return 1; + } + + /* Child, dup stdio to /dev/null and set process group */ + int fd = open("/dev/null", O_RDWR); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + setsid(); + } + + setup_signals(); + mainloop(); + } + } + else + { + print_help(); + } + + return 0; +} diff --git a/tools/remotejoy/pc/remotejoy.o b/tools/remotejoy/pc/remotejoy.o new file mode 100644 index 0000000..8a72fb3 Binary files /dev/null and b/tools/remotejoy/pc/remotejoy.o differ diff --git a/tools/remotejoy/pc/sidewinder.map b/tools/remotejoy/pc/sidewinder.map new file mode 100644 index 0000000..db77620 --- /dev/null +++ b/tools/remotejoy/pc/sidewinder.map @@ -0,0 +1,17 @@ +# Mapping file for Sidewinder Gamepad USB + +# Specify the digital axis +digital:0 + +# Specify the analog joystick +# analog:0 + +# Specify the button mappings +square:4 +triangle:5 +cross:1 +circle:2 +ltrig:6 +rtrig:7 +start:9 +select:8 diff --git a/tools/remotejoy/pc/xpad.map b/tools/remotejoy/pc/xpad.map new file mode 100644 index 0000000..807280e --- /dev/null +++ b/tools/remotejoy/pc/xpad.map @@ -0,0 +1,11 @@ +tol:10000 +digital:3 +cross:0 +circle:1 +triangle:4 +square:3 +ltrig:5 +rtrig:2 +start:6 +select:9 +analog:0 diff --git a/tools/remotejoy/pcsdl/Makefile b/tools/remotejoy/pcsdl/Makefile new file mode 100644 index 0000000..5024d10 --- /dev/null +++ b/tools/remotejoy/pcsdl/Makefile @@ -0,0 +1,19 @@ +OUTPUT=remotejoy +OBJS=remotejoy.o font.o + +CFLAGS=-O2 -Wall -g $(shell sdl-config --cflags) + +PREFIX=$(shell psp-config --pspdev-path 2> /dev/null) + +all: $(OUTPUT) + +$(OUTPUT): $(OBJS) + $(CC) -o $@ $^ $(shell sdl-config --libs) + +install: $(OUTPUT) + @echo "Installing $(OUTPUT)..." + @if ( test $(PREFIX) ); then { mkdir -p $(PREFIX)/bin && cp $(OUTPUT) $(PREFIX)/bin; } else { echo "Error: psp-config not found!"; exit 1; } fi + @echo "Done!" + +clean: + rm -f $(OUTPUT) *.o diff --git a/tools/remotejoy/pcsdl/font.c b/tools/remotejoy/pcsdl/font.c new file mode 100644 index 0000000..278bce5 --- /dev/null +++ b/tools/remotejoy/pcsdl/font.c @@ -0,0 +1,244 @@ +/* Font nabbed for PSPLINK */ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * font.c - Debug Font. + * + * Copyright (c) 2005 Marcus R. Brown + * Copyright (c) 2005 James Forshaw + * Copyright (c) 2005 John Kelley + * + * $Id: font.c 2187 2007-02-20 19:28:00Z tyranid $ + */ +#include +#include +#include +#include + +#define FONT_X 8 +#define FONT_Y 8 + +static uint8_t g_font[]= +"\x00\x00\x00\x00\x00\x00\x00\x00\x3c\x42\xa5\x81\xa5\x99\x42\x3c" +"\x3c\x7e\xdb\xff\xff\xdb\x66\x3c\x6c\xfe\xfe\xfe\x7c\x38\x10\x00" +"\x10\x38\x7c\xfe\x7c\x38\x10\x00\x10\x38\x54\xfe\x54\x10\x38\x00" +"\x10\x38\x7c\xfe\xfe\x10\x38\x00\x00\x00\x00\x30\x30\x00\x00\x00" +"\xff\xff\xff\xe7\xe7\xff\xff\xff\x38\x44\x82\x82\x82\x44\x38\x00" +"\xc7\xbb\x7d\x7d\x7d\xbb\xc7\xff\x0f\x03\x05\x79\x88\x88\x88\x70" +"\x38\x44\x44\x44\x38\x10\x7c\x10\x30\x28\x24\x24\x28\x20\xe0\xc0" +"\x3c\x24\x3c\x24\x24\xe4\xdc\x18\x10\x54\x38\xee\x38\x54\x10\x00" +"\x10\x10\x10\x7c\x10\x10\x10\x10\x10\x10\x10\xff\x00\x00\x00\x00" +"\x00\x00\x00\xff\x10\x10\x10\x10\x10\x10\x10\xf0\x10\x10\x10\x10" +"\x10\x10\x10\x1f\x10\x10\x10\x10\x10\x10\x10\xff\x10\x10\x10\x10" +"\x10\x10\x10\x10\x10\x10\x10\x10\x00\x00\x00\xff\x00\x00\x00\x00" +"\x00\x00\x00\x1f\x10\x10\x10\x10\x00\x00\x00\xf0\x10\x10\x10\x10" +"\x10\x10\x10\x1f\x00\x00\x00\x00\x10\x10\x10\xf0\x00\x00\x00\x00" +"\x81\x42\x24\x18\x18\x24\x42\x81\x01\x02\x04\x08\x10\x20\x40\x80" +"\x80\x40\x20\x10\x08\x04\x02\x01\x00\x10\x10\xff\x10\x10\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x20\x20\x20\x20\x00\x00\x20\x00" +"\x50\x50\x50\x00\x00\x00\x00\x00\x50\x50\xf8\x50\xf8\x50\x50\x00" +"\x20\x78\xa0\x70\x28\xf0\x20\x00\xc0\xc8\x10\x20\x40\x98\x18\x00" +"\x40\xa0\x40\xa8\x90\x98\x60\x00\x10\x20\x40\x00\x00\x00\x00\x00" +"\x10\x20\x40\x40\x40\x20\x10\x00\x40\x20\x10\x10\x10\x20\x40\x00" +"\x20\xa8\x70\x20\x70\xa8\x20\x00\x00\x20\x20\xf8\x20\x20\x00\x00" +"\x00\x00\x00\x00\x00\x20\x20\x40\x00\x00\x00\x78\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x60\x60\x00\x00\x00\x08\x10\x20\x40\x80\x00" +"\x70\x88\x98\xa8\xc8\x88\x70\x00\x20\x60\xa0\x20\x20\x20\xf8\x00" +"\x70\x88\x08\x10\x60\x80\xf8\x00\x70\x88\x08\x30\x08\x88\x70\x00" +"\x10\x30\x50\x90\xf8\x10\x10\x00\xf8\x80\xe0\x10\x08\x10\xe0\x00" +"\x30\x40\x80\xf0\x88\x88\x70\x00\xf8\x88\x10\x20\x20\x20\x20\x00" +"\x70\x88\x88\x70\x88\x88\x70\x00\x70\x88\x88\x78\x08\x10\x60\x00" +"\x00\x00\x20\x00\x00\x20\x00\x00\x00\x00\x20\x00\x00\x20\x20\x40" +"\x18\x30\x60\xc0\x60\x30\x18\x00\x00\x00\xf8\x00\xf8\x00\x00\x00" +"\xc0\x60\x30\x18\x30\x60\xc0\x00\x70\x88\x08\x10\x20\x00\x20\x00" +"\x70\x88\x08\x68\xa8\xa8\x70\x00\x20\x50\x88\x88\xf8\x88\x88\x00" +"\xf0\x48\x48\x70\x48\x48\xf0\x00\x30\x48\x80\x80\x80\x48\x30\x00" +"\xe0\x50\x48\x48\x48\x50\xe0\x00\xf8\x80\x80\xf0\x80\x80\xf8\x00" +"\xf8\x80\x80\xf0\x80\x80\x80\x00\x70\x88\x80\xb8\x88\x88\x70\x00" +"\x88\x88\x88\xf8\x88\x88\x88\x00\x70\x20\x20\x20\x20\x20\x70\x00" +"\x38\x10\x10\x10\x90\x90\x60\x00\x88\x90\xa0\xc0\xa0\x90\x88\x00" +"\x80\x80\x80\x80\x80\x80\xf8\x00\x88\xd8\xa8\xa8\x88\x88\x88\x00" +"\x88\xc8\xc8\xa8\x98\x98\x88\x00\x70\x88\x88\x88\x88\x88\x70\x00" +"\xf0\x88\x88\xf0\x80\x80\x80\x00\x70\x88\x88\x88\xa8\x90\x68\x00" +"\xf0\x88\x88\xf0\xa0\x90\x88\x00\x70\x88\x80\x70\x08\x88\x70\x00" +"\xf8\x20\x20\x20\x20\x20\x20\x00\x88\x88\x88\x88\x88\x88\x70\x00" +"\x88\x88\x88\x88\x50\x50\x20\x00\x88\x88\x88\xa8\xa8\xd8\x88\x00" +"\x88\x88\x50\x20\x50\x88\x88\x00\x88\x88\x88\x70\x20\x20\x20\x00" +"\xf8\x08\x10\x20\x40\x80\xf8\x00\x70\x40\x40\x40\x40\x40\x70\x00" +"\x00\x00\x80\x40\x20\x10\x08\x00\x70\x10\x10\x10\x10\x10\x70\x00" +"\x20\x50\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00" +"\x40\x20\x10\x00\x00\x00\x00\x00\x00\x00\x70\x08\x78\x88\x78\x00" +"\x80\x80\xb0\xc8\x88\xc8\xb0\x00\x00\x00\x70\x88\x80\x88\x70\x00" +"\x08\x08\x68\x98\x88\x98\x68\x00\x00\x00\x70\x88\xf8\x80\x70\x00" +"\x10\x28\x20\xf8\x20\x20\x20\x00\x00\x00\x68\x98\x98\x68\x08\x70" +"\x80\x80\xf0\x88\x88\x88\x88\x00\x20\x00\x60\x20\x20\x20\x70\x00" +"\x10\x00\x30\x10\x10\x10\x90\x60\x40\x40\x48\x50\x60\x50\x48\x00" +"\x60\x20\x20\x20\x20\x20\x70\x00\x00\x00\xd0\xa8\xa8\xa8\xa8\x00" +"\x00\x00\xb0\xc8\x88\x88\x88\x00\x00\x00\x70\x88\x88\x88\x70\x00" +"\x00\x00\xb0\xc8\xc8\xb0\x80\x80\x00\x00\x68\x98\x98\x68\x08\x08" +"\x00\x00\xb0\xc8\x80\x80\x80\x00\x00\x00\x78\x80\xf0\x08\xf0\x00" +"\x40\x40\xf0\x40\x40\x48\x30\x00\x00\x00\x90\x90\x90\x90\x68\x00" +"\x00\x00\x88\x88\x88\x50\x20\x00\x00\x00\x88\xa8\xa8\xa8\x50\x00" +"\x00\x00\x88\x50\x20\x50\x88\x00\x00\x00\x88\x88\x98\x68\x08\x70" +"\x00\x00\xf8\x10\x20\x40\xf8\x00\x18\x20\x20\x40\x20\x20\x18\x00" +"\x20\x20\x20\x00\x20\x20\x20\x00\xc0\x20\x20\x10\x20\x20\xc0\x00" +"\x40\xa8\x10\x00\x00\x00\x00\x00\x00\x00\x20\x50\xf8\x00\x00\x00" +"\x70\x88\x80\x80\x88\x70\x20\x60\x90\x00\x00\x90\x90\x90\x68\x00" +"\x10\x20\x70\x88\xf8\x80\x70\x00\x20\x50\x70\x08\x78\x88\x78\x00" +"\x48\x00\x70\x08\x78\x88\x78\x00\x20\x10\x70\x08\x78\x88\x78\x00" +"\x20\x00\x70\x08\x78\x88\x78\x00\x00\x70\x80\x80\x80\x70\x10\x60" +"\x20\x50\x70\x88\xf8\x80\x70\x00\x50\x00\x70\x88\xf8\x80\x70\x00" +"\x20\x10\x70\x88\xf8\x80\x70\x00\x50\x00\x00\x60\x20\x20\x70\x00" +"\x20\x50\x00\x60\x20\x20\x70\x00\x40\x20\x00\x60\x20\x20\x70\x00" +"\x50\x00\x20\x50\x88\xf8\x88\x00\x20\x00\x20\x50\x88\xf8\x88\x00" +"\x10\x20\xf8\x80\xf0\x80\xf8\x00\x00\x00\x6c\x12\x7e\x90\x6e\x00" +"\x3e\x50\x90\x9c\xf0\x90\x9e\x00\x60\x90\x00\x60\x90\x90\x60\x00" +"\x90\x00\x00\x60\x90\x90\x60\x00\x40\x20\x00\x60\x90\x90\x60\x00" +"\x40\xa0\x00\xa0\xa0\xa0\x50\x00\x40\x20\x00\xa0\xa0\xa0\x50\x00" +"\x90\x00\x90\x90\xb0\x50\x10\xe0\x50\x00\x70\x88\x88\x88\x70\x00" +"\x50\x00\x88\x88\x88\x88\x70\x00\x20\x20\x78\x80\x80\x78\x20\x20" +"\x18\x24\x20\xf8\x20\xe2\x5c\x00\x88\x50\x20\xf8\x20\xf8\x20\x00" +"\xc0\xa0\xa0\xc8\x9c\x88\x88\x8c\x18\x20\x20\xf8\x20\x20\x20\x40" +"\x10\x20\x70\x08\x78\x88\x78\x00\x10\x20\x00\x60\x20\x20\x70\x00" +"\x20\x40\x00\x60\x90\x90\x60\x00\x20\x40\x00\x90\x90\x90\x68\x00" +"\x50\xa0\x00\xa0\xd0\x90\x90\x00\x28\x50\x00\xc8\xa8\x98\x88\x00" +"\x00\x70\x08\x78\x88\x78\x00\xf8\x00\x60\x90\x90\x90\x60\x00\xf0" +"\x20\x00\x20\x40\x80\x88\x70\x00\x00\x00\x00\xf8\x80\x80\x00\x00" +"\x00\x00\x00\xf8\x08\x08\x00\x00\x84\x88\x90\xa8\x54\x84\x08\x1c" +"\x84\x88\x90\xa8\x58\xa8\x3c\x08\x20\x00\x00\x20\x20\x20\x20\x00" +"\x00\x00\x24\x48\x90\x48\x24\x00\x00\x00\x90\x48\x24\x48\x90\x00" +"\x28\x50\x20\x50\x88\xf8\x88\x00\x28\x50\x70\x08\x78\x88\x78\x00" +"\x28\x50\x00\x70\x20\x20\x70\x00\x28\x50\x00\x20\x20\x20\x70\x00" +"\x28\x50\x00\x70\x88\x88\x70\x00\x50\xa0\x00\x60\x90\x90\x60\x00" +"\x28\x50\x00\x88\x88\x88\x70\x00\x50\xa0\x00\xa0\xa0\xa0\x50\x00" +"\xfc\x48\x48\x48\xe8\x08\x50\x20\x00\x50\x00\x50\x50\x50\x10\x20" +"\xc0\x44\xc8\x54\xec\x54\x9e\x04\x10\xa8\x40\x00\x00\x00\x00\x00" +"\x00\x20\x50\x88\x50\x20\x00\x00\x88\x10\x20\x40\x80\x28\x00\x00" +"\x7c\xa8\xa8\x68\x28\x28\x28\x00\x38\x40\x30\x48\x48\x30\x08\x70" +"\x00\x00\x00\x00\x00\x00\xff\xff\xf0\xf0\xf0\xf0\x0f\x0f\x0f\x0f" +"\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x3c\x3c\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00" +"\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x0f\x0f\x0f\x0f\xf0\xf0\xf0\xf0" +"\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\x03\x03\x03\x03\x03\x03\x03\x03" +"\x3f\x3f\x3f\x3f\x3f\x3f\x3f\x3f\x11\x22\x44\x88\x11\x22\x44\x88" +"\x88\x44\x22\x11\x88\x44\x22\x11\xfe\x7c\x38\x10\x00\x00\x00\x00" +"\x00\x00\x00\x00\x10\x38\x7c\xfe\x80\xc0\xe0\xf0\xe0\xc0\x80\x00" +"\x01\x03\x07\x0f\x07\x03\x01\x00\xff\x7e\x3c\x18\x18\x3c\x7e\xff" +"\x81\xc3\xe7\xff\xff\xe7\xc3\x81\xf0\xf0\xf0\xf0\x00\x00\x00\x00" +"\x00\x00\x00\x00\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x00\x00\x00\x00" +"\x00\x00\x00\x00\xf0\xf0\xf0\xf0\x33\x33\xcc\xcc\x33\x33\xcc\xcc" +"\x00\x20\x20\x50\x50\x88\xf8\x00\x20\x20\x70\x20\x70\x20\x20\x00" +"\x00\x00\x00\x50\x88\xa8\x50\x00\xff\xff\xff\xff\xff\xff\xff\xff" +"\x00\x00\x00\x00\xff\xff\xff\xff\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0" +"\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\xff\xff\xff\xff\x00\x00\x00\x00" +"\x00\x00\x68\x90\x90\x90\x68\x00\x30\x48\x48\x70\x48\x48\x70\xc0" +"\xf8\x88\x80\x80\x80\x80\x80\x00\xf8\x50\x50\x50\x50\x50\x98\x00" +"\xf8\x88\x40\x20\x40\x88\xf8\x00\x00\x00\x78\x90\x90\x90\x60\x00" +"\x00\x50\x50\x50\x50\x68\x80\x80\x00\x50\xa0\x20\x20\x20\x20\x00" +"\xf8\x20\x70\xa8\xa8\x70\x20\xf8\x20\x50\x88\xf8\x88\x50\x20\x00" +"\x70\x88\x88\x88\x50\x50\xd8\x00\x30\x40\x40\x20\x50\x50\x50\x20" +"\x00\x00\x00\x50\xa8\xa8\x50\x00\x08\x70\xa8\xa8\xa8\x70\x80\x00" +"\x38\x40\x80\xf8\x80\x40\x38\x00\x70\x88\x88\x88\x88\x88\x88\x00" +"\x00\xf8\x00\xf8\x00\xf8\x00\x00\x20\x20\xf8\x20\x20\x00\xf8\x00" +"\xc0\x30\x08\x30\xc0\x00\xf8\x00\x18\x60\x80\x60\x18\x00\xf8\x00" +"\x10\x28\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\xa0\x40" +"\x00\x20\x00\xf8\x00\x20\x00\x00\x00\x50\xa0\x00\x50\xa0\x00\x00" +"\x00\x18\x24\x24\x18\x00\x00\x00\x00\x30\x78\x78\x30\x00\x00\x00" +"\x00\x00\x00\x00\x30\x00\x00\x00\x3e\x20\x20\x20\xa0\x60\x20\x00" +"\xa0\x50\x50\x50\x00\x00\x00\x00\x40\xa0\x20\x40\xe0\x00\x00\x00" +"\x00\x38\x38\x38\x38\x38\x38\x00\x00\x00\x00\x00\x00\x00\x00"; + +static SDL_Surface *pFont = NULL; +static int total_chars = 0; + +void init_font(void) +{ + Uint32 rmask, gmask, bmask, amask; + int width; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; +#else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; +#endif + width = sizeof(g_font); + total_chars = sizeof(g_font) / FONT_Y; + + pFont = SDL_CreateRGBSurface(SDL_SWSURFACE, width, FONT_Y, 32, rmask, gmask, bmask, amask); + if(pFont) + { + int x, y; + int i; + uint32_t *pixel; + + SDL_LockSurface(pFont); + pixel = (uint32_t*) pFont->pixels; + + for(i = 0; i < sizeof(g_font); i += FONT_Y) + { + for(y = 0; y < FONT_Y; y++) + { + for(x = 0; x < FONT_X; x++) + { + int bit; + + bit = g_font[i+y] & (128 >> x); + if(bit) + { + pixel[(width * y) + x] = 0xFFFFFFFF; + } + else + { + pixel[(width * y) + x] = 0; + } + } + } + + pixel += FONT_X; + } + + SDL_UnlockSurface(pFont); + } +} + +void put_char_xy(SDL_Surface *screen, int x, int y, unsigned char ch) +{ + SDL_Rect src; + SDL_Rect dest; + + src.x = FONT_X * ch; + src.y = 0; + src.w = FONT_X; + src.h = FONT_Y; + dest.x = x; + dest.y = y; + + SDL_BlitSurface(pFont, &src, screen, &dest); +} + +void print_text(SDL_Surface *screen, int x, int y, const char *fmt, ...) +{ + va_list vl; + char buf[1024]; + int i; + + va_start(vl, fmt); + vsnprintf(buf, sizeof(buf), fmt, vl); + i = 0; + while(buf[i]) + { + put_char_xy(screen, x, y, buf[i]); + x += FONT_X; + i++; + } + + va_end(vl); +} diff --git a/tools/remotejoy/pcsdl/font.o b/tools/remotejoy/pcsdl/font.o new file mode 100644 index 0000000..19593af Binary files /dev/null and b/tools/remotejoy/pcsdl/font.o differ diff --git a/tools/remotejoy/pcsdl/ps2.map b/tools/remotejoy/pcsdl/ps2.map new file mode 100644 index 0000000..0a8e595 --- /dev/null +++ b/tools/remotejoy/pcsdl/ps2.map @@ -0,0 +1,20 @@ +triangle:0 +circle:1 +cross:2 +square:3 +ltrig:4 +rtrig:5 +ltrig:6 +rtrig:7 +select:8 +start:9 +ltrig:10 +rtrig:11 +up:12 +right:13 +down:14 +left:15 + +analog:0 +digital:1 +tol:10000 \ No newline at end of file diff --git a/tools/remotejoy/pcsdl/remotejoy b/tools/remotejoy/pcsdl/remotejoy new file mode 100755 index 0000000..5e149b8 Binary files /dev/null and b/tools/remotejoy/pcsdl/remotejoy differ diff --git a/tools/remotejoy/pcsdl/remotejoy.c b/tools/remotejoy/pcsdl/remotejoy.c new file mode 100644 index 0000000..c5b2699 --- /dev/null +++ b/tools/remotejoy/pcsdl/remotejoy.c @@ -0,0 +1,1355 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * remotejoy.c - PSPLINK PC remote joystick handler (SDL Version) + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/tools/remotejoy/pcsdl/remotejoy.c $ + * $Id: remotejoy.c 2187 2007-02-20 19:28:00Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../remotejoy.h" + +#define DEFAULT_PORT 10004 +#define DEFAULT_IP "localhost" + +#define MAX_AXES_NUM 32767 +#define DIGITAL_TOL 10000 + +#define PSP_SCREEN_W 480 +#define PSP_SCREEN_H 272 + +#define EVENT_ENABLE_SCREEN 1 +#define EVENT_RENDER_FRAME_1 2 +#define EVENT_RENDER_FRAME_2 3 +#define EVENT_DISABLE_SCREEN 4 + +#ifndef SOL_TCP + #define SOL_TCP IPPROTO_TCP +#endif + +#if defined BUILD_BIGENDIAN || defined _BIG_ENDIAN +uint16_t swap16(uint16_t i) +{ + uint8_t *p = (uint8_t *) &i; + uint16_t ret; + + ret = (p[1] << 8) | p[0]; + + return ret; +} + +uint32_t swap32(uint32_t i) +{ + uint8_t *p = (uint8_t *) &i; + uint32_t ret; + + ret = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; + + return ret; +} + +uint64_t swap64(uint64_t i) +{ + uint8_t *p = (uint8_t *) &i; + uint64_t ret; + + ret = (uint64_t) p[0] | ((uint64_t) p[1] << 8) | ((uint64_t) p[2] << 16) | ((uint64_t) p[3] << 24) + | ((uint64_t) p[4] << 32) | ((uint64_t) p[5] << 40) | ((uint64_t) p[6] << 48) | ((uint64_t) p[7] << 56); + + return ret; +} +#define LE16(x) swap16(x) +#define LE32(x) swap32(x) +#define LE64(x) swap64(x) +#else +#define LE16(x) (x) +#define LE32(x) (x) +#define LE64(x) (x) +#endif + +enum PspCtrlButtons +{ + /** Select button. */ + PSP_CTRL_SELECT = 0x000001, + /** Start button. */ + PSP_CTRL_START = 0x000008, + /** Up D-Pad button. */ + PSP_CTRL_UP = 0x000010, + /** Right D-Pad button. */ + PSP_CTRL_RIGHT = 0x000020, + /** Down D-Pad button. */ + PSP_CTRL_DOWN = 0x000040, + /** Left D-Pad button. */ + PSP_CTRL_LEFT = 0x000080, + /** Left trigger. */ + PSP_CTRL_LTRIGGER = 0x000100, + /** Right trigger. */ + PSP_CTRL_RTRIGGER = 0x000200, + /** Triangle button. */ + PSP_CTRL_TRIANGLE = 0x001000, + /** Circle button. */ + PSP_CTRL_CIRCLE = 0x002000, + /** Cross button. */ + PSP_CTRL_CROSS = 0x004000, + /** Square button. */ + PSP_CTRL_SQUARE = 0x008000, + /** Home button. */ + PSP_CTRL_HOME = 0x010000, + /** Music Note button. */ + PSP_CTRL_NOTE = 0x800000, + /** Screen button. */ + PSP_CTRL_SCREEN = 0x400000, + /** Volume up button. */ + PSP_CTRL_VOLUP = 0x100000, + /** Volume down button. */ + PSP_CTRL_VOLDOWN = 0x200000, +}; + +enum PspButtons +{ + PSP_BUTTON_CROSS = 0, + PSP_BUTTON_CIRCLE = 1, + PSP_BUTTON_TRIANGLE = 2, + PSP_BUTTON_SQUARE = 3, + PSP_BUTTON_LTRIGGER = 4, + PSP_BUTTON_RTRIGGER = 5, + PSP_BUTTON_START = 6, + PSP_BUTTON_SELECT = 7, + PSP_BUTTON_UP = 8, + PSP_BUTTON_DOWN = 9, + PSP_BUTTON_LEFT = 10, + PSP_BUTTON_RIGHT = 11, + PSP_BUTTON_HOME = 12, + PSP_BUTTON_NOTE = 13, + PSP_BUTTON_SCREEN = 14, + PSP_BUTTON_VOLUP = 15, + PSP_BUTTON_VOLDOWN = 16, + PSP_BUTTON_MAX = 17 +}; + +unsigned int g_bitmap[PSP_BUTTON_MAX] = { + PSP_CTRL_CROSS, PSP_CTRL_CIRCLE, PSP_CTRL_TRIANGLE, PSP_CTRL_SQUARE, + PSP_CTRL_LTRIGGER, PSP_CTRL_RTRIGGER, PSP_CTRL_START, PSP_CTRL_SELECT, + PSP_CTRL_UP, PSP_CTRL_DOWN, PSP_CTRL_LEFT, PSP_CTRL_RIGHT, PSP_CTRL_HOME, + PSP_CTRL_NOTE, PSP_CTRL_SCREEN, PSP_CTRL_VOLUP, PSP_CTRL_VOLDOWN +}; + +const char *map_names[PSP_BUTTON_MAX] = { + "cross", "circle", "triangle", "square", + "ltrig", "rtrig", "start", "select", + "up", "down", "left", "right", "home", + "note", "screen", "volup", "voldown" +}; + +/* Maps the buttons on the joystick to the buttons on the PSP controller */ +unsigned int *g_buttmap = NULL; + +struct Args +{ + const char *ip; + unsigned short port; + const char *dev; + const char *mapfile; + int verbose; + int video; + int fullscreen; + int droprate; + int fullcolour; + int halfsize; + int showfps; +}; + +struct GlobalContext +{ + struct Args args; + struct sockaddr_in serv; + char name[128]; + unsigned int version; + unsigned char axes; + unsigned char buttons; + int exit; + int digital; + int analog; + int tol; + int scron; +}; + +struct GlobalContext g_context; + +struct ScreenBuffer +{ + unsigned char buf[PSP_SCREEN_W * PSP_SCREEN_H * 4]; + struct JoyScrHeader head; + /* Mutex? */ +}; + +static struct ScreenBuffer g_buffers[2]; + +void init_font(void); +void print_text(SDL_Surface *screen, int x, int y, const char *fmt, ...); + +/* Should have a mutex on each screen */ + +#define VERBOSE (g_context.args.verbose) + +int fixed_write(int s, const void *buf, int len) +{ + int written = 0; + + while(written < len) + { + int ret; + + ret = write(s, buf+written, len-written); + if(ret < 0) + { + if(errno != EINTR) + { + perror("write"); + written = -1; + break; + } + } + else + { + written += ret; + } + } + + return written; +} + +int parse_args(int argc, char **argv, struct Args *args) +{ + memset(args, 0, sizeof(*args)); + args->ip = DEFAULT_IP; + args->port = DEFAULT_PORT; + + while(1) + { + int ch; + int error = 0; + + ch = getopt(argc, argv, "vsfchldp:i:m:r:"); + + if(ch < 0) + { + break; + } + + switch(ch) + { + case 'p': args->port = atoi(optarg); + break; + case 'i': args->ip = optarg; + break; + case 'm': args->mapfile = optarg; + break; + case 'v': args->verbose = 1; + break; + case 'd': args->video = 1; + break; + case 'f': args->fullscreen = 1; + break; + case 'c': args->fullcolour = 1; + break; + case 'l': args->halfsize = 1; + break; + case 's': args->showfps = 1; + break; + case 'r': args->droprate = atoi(optarg); + if((args->droprate < 0) || (args->droprate > 59)) + { + fprintf(stderr, "Invalid drop rate (0 <= r < 60)\n"); + error = 1; + } + break; + case 'h': + default : error = 1; + break; + }; + + if(error) + { + return 0; + } + } + + argc -= optind; + argv += optind; + + return 1; +} + +void print_help(void) +{ + fprintf(stderr, "Remotejoy Help\n"); + fprintf(stderr, "Usage: remotejoy [options]\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "-p port : Specify the port number\n"); + fprintf(stderr, "-i ip : Specify the ip address (default %s)\n", DEFAULT_IP); + fprintf(stderr, "-m mapfile : Specify a file to map joystick buttons to the PSP\n"); + fprintf(stderr, "-d : Auto enable display support\n"); + fprintf(stderr, "-f : Full screen mode\n"); + fprintf(stderr, "-r drop : Frame Skip, 0 (auto), 1 (1/2), 2 (1/3), 3(1/4) etc.\n"); + fprintf(stderr, "-c : Full colour mode\n"); + fprintf(stderr, "-l : Half size mode (both X and Y)\n"); + fprintf(stderr, "-s : Show fps\n"); + fprintf(stderr, "-v : Verbose mode\n"); +} + +int init_sockaddr(struct sockaddr_in *name, const char *ipaddr, unsigned short port) +{ + struct hostent *hostinfo; + + name->sin_family = AF_INET; + name->sin_port = htons(port); + hostinfo = gethostbyname(ipaddr); + if(hostinfo == NULL) + { + fprintf(stderr, "Unknown host %s\n", ipaddr); + return 0; + } + name->sin_addr = *(struct in_addr *) hostinfo->h_addr; + + return 1; +} + +int connect_to(const char *ipaddr, unsigned short port) +{ + struct sockaddr_in name; + int sock = -1; + int flag = 1; + + sock = socket(PF_INET, SOCK_STREAM, 0); + + if(sock < 0) + { + perror("socket"); + return -1; + } + + if(!init_sockaddr(&name, ipaddr, port)) + { + printf("Could not initialise socket address\n"); + close(sock); + return -1; + } + + if(connect(sock, (struct sockaddr *) &name, sizeof(name)) < 0) + { + perror("connect"); + close(sock); + return -1; + } + + /* Disable NAGLE's algorithm to prevent the packets being joined */ + setsockopt(sock, SOL_TCP, TCP_NODELAY, &flag, sizeof(int)); + + return sock; +} + +int get_joyinfo(SDL_Joystick *stick) +{ + const char *name; + + name = SDL_JoystickName(0); + if(!name) + { + return 0; + } + + strcpy(g_context.name, name); + + g_context.axes = SDL_JoystickNumAxes(stick); + g_context.buttons = SDL_JoystickNumButtons(stick); + + return 1; +} + +void remove_wsp(char *buf) +{ + int len = strlen(buf); + int i = 0; + + while(isspace(buf[i])) + { + i++; + } + + if(i > 0) + { + len -= i; + memmove(buf, &buf[i], len + 1); + } + + if(len <= 0) + { + return; + } + + i = len-1; + while(isspace(buf[i])) + { + buf[i--] = 0; + } +} + +int build_map(const char *mapfile, int buttons) +{ + int i; + FILE *fp; + + g_context.analog = -1; + g_context.digital = -1; + g_context.tol = DIGITAL_TOL; + + g_buttmap = (unsigned int *) malloc(buttons * sizeof(unsigned int)); + if(g_buttmap == NULL) + { + return 0; + } + + for(i = 0; i < buttons; i++) + { + /* Fill with mappings, repeat if more than 8 buttons */ + g_buttmap[i] = i % 8; + } + + if(mapfile) + { + char buffer[512]; + int line = 0; + + fp = fopen(mapfile, "r"); + if(fp == NULL) + { + fprintf(stderr, "Could not open mapfile %s\n", mapfile); + return 0; + } + + while(fgets(buffer, sizeof(buffer), fp)) + { + char *tok, *val; + int butt; + line++; + remove_wsp(buffer); + + if((buffer[0] == '#') || (buffer[0] == 0)) /* Comment or empty line */ + { + continue; + } + + tok = strtok(buffer, ":"); + val = strtok(NULL, ""); + if((tok == NULL) || (val == NULL)) + { + printf("Invalid mapping on line %d\n", line); + continue; + } + + butt = atoi(val); + for(i = 0; i < PSP_BUTTON_MAX; i++) + { + if(strcasecmp(map_names[i], tok) == 0) + { + g_buttmap[butt] = i; + break; + } + } + + if(i == PSP_BUTTON_MAX) + { + if(strcasecmp("analog", tok) == 0) + { + g_context.analog = butt; + } + else if(strcasecmp("digital", tok) == 0) + { + g_context.digital = butt; + } + else if(strcasecmp("tol", tok) == 0) + { + g_context.tol = atoi(val); + } + else + { + fprintf(stderr, "Unknown map type %s\n", tok); + } + } + } + fclose(fp); + } + + return 1; +} + +int send_event(int sock, int type, unsigned int value) +{ + struct JoyEvent event; + + if(sock < 0) + { + return 0; + } + + /* Note, should swap endian */ + event.magic = LE32(JOY_MAGIC); + event.type = LE32(type); + event.value = LE32(value); + + if(fixed_write(sock, &event, sizeof(event)) != sizeof(event)) + { + fprintf(stderr, "Could not write out data to socket\n"); + return 0; + } + + return 1; +} + +void post_event(int no) +{ + SDL_Event event; + + event.type = SDL_USEREVENT; + event.user.code = no; + event.user.data1 = NULL; + event.user.data2 = NULL; + + SDL_PushEvent(&event); +} + +int flush_socket(int sock) +{ + /* If we encounter some horrible error which means we are desynced + * then send a video off packet to remotejoy, wait around for a second sucking up + * any more data from the socket and then reenable */ + + return 0; +} + +void update_fps(SDL_Surface *screen) +{ +#define FRAME_VALUES 32 + static Uint32 times[FRAME_VALUES]; + static Uint32 lastticks = 0; + static int index = 0; + Uint32 ticks; + int i; + double fps; + + ticks = SDL_GetTicks(); + times[index] = ticks - lastticks; + index = (index + 1) % FRAME_VALUES; + lastticks = ticks; + + fps = 0.0; + for(i = 0; i < FRAME_VALUES; i++) + { + fps += (double) times[i]; + } + fps /= (double) FRAME_VALUES; + /* Fps is now average frame time */ + fps = 1000.0 / fps; + /* Now frame frequency in Hz */ + + print_text(screen, 0, 0, "Fps: %.2f", fps); +} + +int read_thread(void *p) +{ + int err = 0; + int frame = 0; + fd_set saveset, readset; + int count; + int sock = *(int *) p; + struct JoyScrHeader head; + + FD_ZERO(&saveset); + FD_SET(sock, &saveset); + + while(!err) + { + readset = saveset; + + count = select(FD_SETSIZE, &readset, NULL, NULL, NULL); + + if(count > 0) + { + int ret; + int mode; + int size; + + if(FD_ISSET(sock, &readset)) + { + ret = read(sock, &head, sizeof(head)); + if((ret != sizeof(head)) || (LE32(head.magic) != JOY_MAGIC)) + { + fprintf(stderr, "Error in socket %d, magic %08X\n", ret, head.magic); + flush_socket(sock); + break; + } + + mode = LE32(head.mode); + size = LE32(head.size); + g_buffers[frame].head.mode = mode; + g_buffers[frame].head.size = size; + + if(mode < 0) + { + if(g_context.args.video) + { + post_event(EVENT_ENABLE_SCREEN); + } + else + { + g_context.scron = 0; + } + } + else if(mode > 3) + { + /* Flush socket */ + flush_socket(sock); + } + else + { + /* Try and read in screen */ + /* If we do not get a full frame read and we timeout in quater second or so then + * reset sync as it probably means the rest isn't coming */ + int loc = 0; + + //fprintf(stderr, "Size %d\n", size); + while(1) + { + readset = saveset; + + /* Should have a time out */ + count = select(FD_SETSIZE, &readset, NULL, NULL, NULL); + if(count > 0) + { + ret = read(sock, &(g_buffers[frame].buf[loc]), size-loc); + if(ret < 0) + { + if(errno != EINTR) + { + perror("read:"); + err = 1; + break; + } + } + else if(ret == 0) + { + fprintf(stderr, "EOF\n"); + break; + } + + //fprintf(stderr, "Read %d\n", loc); + loc += ret; + if(loc == size) + { + break; + } + } + else if(count < 0) + { + if(errno != EINTR) + { + perror("select:"); + err = 1; + break; + } + } + } + + if(!err) + { + if(frame) + { + post_event(EVENT_RENDER_FRAME_2); + } + else + { + post_event(EVENT_RENDER_FRAME_1); + } + frame ^= 1; + } + } + } + } + else if(count < 0) + { + if(errno != EINTR) + { + perror("select:"); + err = 1; + } + } + } + + return 0; +} + +SDL_Surface *create_surface(void *buf, int mode) +{ + unsigned int rmask, bmask, gmask, amask; + int currw, currh; + int bpp; + + currw = PSP_SCREEN_W; + currh = PSP_SCREEN_H; + if(g_context.args.halfsize) + { + currw >>= 1; + currh >>= 1; + } + + if(VERBOSE) + { + printf("Mode %d\n", mode); + } + + switch(mode) + { + case 3: + rmask = LE32(0x000000FF); + gmask = LE32(0x0000FF00); + bmask = LE32(0x00FF0000); + amask = 0; + bpp = 32; + break; + case 2: + rmask = LE16(0x000F); + gmask = LE16(0x00F0); + bmask = LE16(0x0F00); + amask = 0; + bpp = 16; + break; + case 1: + rmask = LE16(0x1F); + gmask = LE16(0x1F << 5); + bmask = LE16(0x1F << 10); + amask = 0; + bpp = 16; + break; + case 0: + rmask = LE16(0x1F); + gmask = LE16(0x3F << 5); + bmask = LE16(0x1F << 11); + amask = 0; + bpp = 16; + break; + default: return NULL; + }; + + return SDL_CreateRGBSurfaceFrom(buf, currw, currh, bpp, currw*(bpp/8), + rmask, gmask, bmask, amask); +} + +void save_screenshot(SDL_Surface *surface) +{ + int i; + char path[PATH_MAX]; + struct stat s; + + /* If we cant find one in the next 1000 then dont bother */ + for(i = 0; i < 1000; i++) + { + snprintf(path, PATH_MAX, "scrshot%03d.bmp", i); + if(stat(path, &s) < 0) + { + break; + } + } + + if(i == 1000) + { + return; + } + + if(SDL_SaveBMP(surface, path) == 0) + { + printf("Saved screenshot to %s\n", path); + } + else + { + printf("Error saving screenshot\n"); + } +} + +void mainloop(void) +{ + SDL_Joystick *stick = NULL; + SDL_Surface *screen = NULL; + SDL_Surface *buf1 = NULL; + SDL_Surface *buf2 = NULL; + SDL_Thread *thread = NULL; + int currw, currh; + int sdl_init = 0; + int sock = -1; + unsigned int button_state = 0; + int currmode[2] = { 3, 3 }; + int flags = SDL_HWSURFACE; + int pspflags = 0; + int showfps = 0; + + if(g_context.args.fullscreen) + { + flags |= SDL_FULLSCREEN; + } + + if(g_context.args.fullcolour) + { + pspflags = SCREEN_CMD_FULLCOLOR; + } + + if(g_context.args.halfsize) + { + pspflags |= SCREEN_CMD_HSIZE; + } + + if(g_context.args.droprate) + { + pspflags |= SCREEN_CMD_DROPRATE(g_context.args.droprate); + } + + showfps = g_context.args.showfps; + + do + { + if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) + { + fprintf(stderr, "Could not initialise SDL\n"); + break; + } + + currw = PSP_SCREEN_W; + currh = PSP_SCREEN_H; + if(g_context.args.halfsize) + { + currw >>= 1; + currh >>= 1; + } + + screen = SDL_SetVideoMode(currw, currh, 0, flags); + if(screen == NULL) + { + break; + } + init_font(); + + SDL_ShowCursor(SDL_DISABLE); + + /* Pre-build buffer, we might change these later */ + buf1 = create_surface(g_buffers[0].buf, 3); + if(buf1 == NULL) + { + break; + } + + buf2 = create_surface(g_buffers[1].buf, 3); + if(buf2 == NULL) + { + break; + } + + SDL_WM_SetCaption("RemoteJoySDL - Press any ESC key to exit", NULL); + + sdl_init = 1; + + if(SDL_NumJoysticks() > 0) + { + stick = SDL_JoystickOpen(0); + if(!stick) + { + break; + } + + if(!get_joyinfo(stick)) + { + break; + } + + if(VERBOSE) + { + printf("name: %s, axes: %d, buttons: %d\n", g_context.name, + g_context.axes, g_context.buttons); + } + + if(!build_map(g_context.args.mapfile, g_context.buttons)) + { + break; + } + } + + sock = connect_to(g_context.args.ip, g_context.args.port); + if(sock < 0) + { + break; + } + + thread = SDL_CreateThread(read_thread, (void*) &sock); + if(thread == NULL) + { + break; + } + + while(!g_context.exit) + { + SDL_Event event; + + if(!SDL_WaitEvent(&event)) + { + break; + } + + if(event.type == SDL_VIDEORESIZE) + { + SDL_FreeSurface(screen); + screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 0, flags); + currw = event.resize.w; + currh = event.resize.h; + } + + if(event.type == SDL_USEREVENT) + { + switch(event.user.code) + { + case EVENT_ENABLE_SCREEN: + send_event(sock, TYPE_SCREEN_CMD, SCREEN_CMD_ACTIVE | pspflags); + g_context.scron = 1; + break; + case EVENT_DISABLE_SCREEN: + send_event(sock, TYPE_SCREEN_CMD, 0); + g_context.scron = 0; + break; + case EVENT_RENDER_FRAME_1: + if(currmode[0] != g_buffers[0].head.mode) + { + SDL_FreeSurface(buf1); + buf1 = create_surface(g_buffers[0].buf, g_buffers[0].head.mode); + currmode[0] = g_buffers[0].head.mode; + } + SDL_BlitSurface(buf1, NULL, screen, NULL); + if(showfps) + { + update_fps(screen); + } + SDL_UpdateRect(screen, 0, 0, currw, currh); + break; + case EVENT_RENDER_FRAME_2: + if(currmode[1] != g_buffers[1].head.mode) + { + SDL_FreeSurface(buf2); + buf2 = create_surface(g_buffers[1].buf, g_buffers[1].head.mode); + currmode[1] = g_buffers[1].head.mode; + } + SDL_BlitSurface(buf2, NULL, screen, NULL); + if(showfps) + { + update_fps(screen); + } + SDL_UpdateRect(screen, 0, 0, currw, currh); + break; + default: + fprintf(stderr, "Error, invalid event type\n"); + }; + continue; + } + + if((event.type == SDL_KEYDOWN) || (event.type == SDL_KEYUP)) + { + unsigned int bitmap = 0; + SDL_KeyboardEvent *key = (SDL_KeyboardEvent *) &event; + + if(key->keysym.sym == SDLK_ESCAPE) + { + break; + } + + if(key->keysym.sym == SDLK_F8) + { + if(event.type == SDL_KEYDOWN) + { + SDL_WM_ToggleFullScreen(screen); + } + + continue; + } + + if(key->keysym.sym == SDLK_F3) + { + if(event.type == SDL_KEYDOWN) + { + if(VERBOSE) + { + printf("Switch FullColour Mode\n"); + } + pspflags ^= SCREEN_CMD_FULLCOLOR; + send_event(sock, TYPE_SCREEN_CMD, SCREEN_CMD_ACTIVE | pspflags); + g_context.scron = 1; + g_context.args.fullcolour ^= 1; + } + } + + if(key->keysym.sym == SDLK_F4) + { + if(event.type == SDL_KEYDOWN) + { + if(VERBOSE) + { + printf("Switch Halfsize Mode\n"); + } + currw = PSP_SCREEN_W; + currh = PSP_SCREEN_H; + if((pspflags & SCREEN_CMD_HSIZE) == 0) + { + currw >>= 1; + currh >>= 1; + } + pspflags ^= SCREEN_CMD_HSIZE; + SDL_FreeSurface(screen); + screen = SDL_SetVideoMode(currw, currh, 0, flags); + send_event(sock, TYPE_SCREEN_CMD, SCREEN_CMD_ACTIVE | pspflags); + g_context.scron = 1; + g_context.args.halfsize ^= 1; + /* Force change of buffers */ + currmode[0] = -1; + currmode[1] = -1; + } + } + + if(key->keysym.sym == SDLK_F5) + { + if(event.type == SDL_KEYDOWN) + { + if(g_context.scron) + { + if(VERBOSE) + { + printf("Disable Screen\n"); + } + send_event(sock, TYPE_SCREEN_CMD, 0); + g_context.scron = 0; + } + else + { + if(VERBOSE) + { + printf("Enable Screen\n"); + } + send_event(sock, TYPE_SCREEN_CMD, SCREEN_CMD_ACTIVE | pspflags); + g_context.scron = 1; + } + } + + continue; + } + + if(key->keysym.sym == SDLK_F9) + { + if(event.type == SDL_KEYDOWN) + { + showfps ^= 1; + } + } + + if(key->keysym.sym == SDLK_F10) + { + if(event.type == SDL_KEYDOWN) + { + save_screenshot(screen); + } + } + + switch(key->keysym.sym) + { + case SDLK_LEFT: bitmap = PSP_CTRL_LEFT; + break; + case SDLK_RIGHT: bitmap = PSP_CTRL_RIGHT; + break; + case SDLK_UP: bitmap = PSP_CTRL_UP; + break; + case SDLK_DOWN: bitmap = PSP_CTRL_DOWN; + break; + case SDLK_a: bitmap = PSP_CTRL_SQUARE; + break; + case SDLK_s: bitmap = PSP_CTRL_TRIANGLE; + break; + case SDLK_z: bitmap = PSP_CTRL_CROSS; + break; + case SDLK_x: bitmap = PSP_CTRL_CIRCLE; + break; + case SDLK_q: bitmap = PSP_CTRL_LTRIGGER; + break; + case SDLK_w: bitmap = PSP_CTRL_RTRIGGER; + break; + case SDLK_RETURN: bitmap = PSP_CTRL_START; + break; + case SDLK_SPACE: bitmap = PSP_CTRL_SELECT; + break; + case SDLK_y: bitmap = PSP_CTRL_HOME; + break; + case SDLK_u: bitmap = PSP_CTRL_VOLDOWN; + break; + case SDLK_i: bitmap = PSP_CTRL_VOLUP; + break; + case SDLK_o: bitmap = PSP_CTRL_SCREEN; + break; + case SDLK_p: bitmap = PSP_CTRL_NOTE; + break; + default: break; + }; + + if(event.type == SDL_KEYDOWN) + { + button_state |= bitmap; + if(!send_event(sock, TYPE_BUTTON_DOWN, bitmap)) + { + break; + } + } + else + { + button_state &= ~bitmap; + if(!send_event(sock, TYPE_BUTTON_UP, bitmap)) + { + break; + } + } + } + + if(event.type == SDL_QUIT) + { + break; + } + + if((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP)) + { + unsigned int bitmap = g_bitmap[g_buttmap[event.jbutton.button]]; + if(event.type == SDL_JOYBUTTONDOWN) + { + button_state |= bitmap; + if(!send_event(sock, TYPE_BUTTON_DOWN, bitmap)) + { + break; + } + } + else + { + button_state &= ~bitmap; + if(!send_event(sock, TYPE_BUTTON_UP, bitmap)) + { + break; + } + } + + if(VERBOSE) + { + printf("Button %d event\n", event.jbutton.button); + printf("Button state: %08X\n", button_state); + } + } + else if(event.type == SDL_JOYAXISMOTION) + { + unsigned int curr_butt = 0; + + if(VERBOSE) + { + printf("Axis %d event\n", event.jaxis.axis/2); + } + + if(g_context.digital == (event.jaxis.axis/2)) + { + if(event.jaxis.axis & 1) + { + if(event.jaxis.value > g_context.tol) + { + curr_butt = PSP_CTRL_DOWN; + } + else if(event.jaxis.value < -g_context.tol) + { + curr_butt = PSP_CTRL_UP; + } + else + { + /* Do nothing */ + } + + if((button_state & PSP_CTRL_UP) && (curr_butt != PSP_CTRL_UP)) + { + /* Send UP up */ + button_state &= ~PSP_CTRL_UP; + if(!send_event(sock, TYPE_BUTTON_UP, PSP_CTRL_UP)) + { + break; + } + } + + if((button_state & PSP_CTRL_DOWN) && (curr_butt != PSP_CTRL_DOWN)) + { + /* Send DOWN up */ + button_state &= ~PSP_CTRL_DOWN; + if(!send_event(sock, TYPE_BUTTON_UP, PSP_CTRL_DOWN)) + { + break; + } + } + + if(((button_state & curr_butt) == 0) && (curr_butt != 0)) + { + /* Send down */ + button_state |= curr_butt; + if(!send_event(sock, TYPE_BUTTON_DOWN, curr_butt)) + { + break; + } + } + } + else + { + if(event.jaxis.value > g_context.tol) + { + curr_butt = PSP_CTRL_RIGHT; + } + else if(event.jaxis.value < -g_context.tol) + { + curr_butt = PSP_CTRL_LEFT; + } + else + { + /* Do nothing */ + } + + if((button_state & PSP_CTRL_RIGHT) && (curr_butt != PSP_CTRL_RIGHT)) + { + /* Send right up */ + button_state &= ~PSP_CTRL_RIGHT; + if(!send_event(sock, TYPE_BUTTON_UP, PSP_CTRL_RIGHT)) + { + break; + } + } + + if((button_state & PSP_CTRL_LEFT) && (curr_butt != PSP_CTRL_LEFT)) + { + /* Send left up */ + button_state &= ~PSP_CTRL_LEFT; + if(!send_event(sock, TYPE_BUTTON_UP, PSP_CTRL_LEFT)) + { + break; + } + } + + if(((button_state & curr_butt) == 0) && (curr_butt != 0)) + { + /* Send down */ + button_state |= curr_butt; + if(!send_event(sock, TYPE_BUTTON_DOWN, curr_butt)) + { + break; + } + } + } + } + + if(g_context.analog == (event.jaxis.axis / 2)) + { + int val = event.jaxis.value + MAX_AXES_NUM; + if(event.jaxis.axis & 1) + { + /* Send Ly (val * 255) / (MAX_AXES_NUM * 2)) */ + if(!send_event(sock, TYPE_ANALOG_Y, (val * 255) / (MAX_AXES_NUM * 2))) + { + break; + } + } + else + { + /* Send Lx (val * 255) / (MAX_AXES_NUM * 2)) */ + if(!send_event(sock, TYPE_ANALOG_X, (val * 255) / (MAX_AXES_NUM * 2))) + { + break; + } + } + } + } + } + } + while(0); + + if(stick) + { + SDL_JoystickClose(stick); + } + + if(buf1) + { + SDL_FreeSurface(buf1); + buf1 = NULL; + } + + if(buf2) + { + SDL_FreeSurface(buf2); + buf2 = NULL; + } + + if(thread) + { + SDL_KillThread(thread); + } + + if(sdl_init) + { + SDL_Quit(); + } + + if(sock >= 0) + { + close(sock); + } + + if(g_buttmap) + { + free(g_buttmap); + } + + printf("Exiting\n"); +} + +int main(int argc, char **argv) +{ + fprintf(stderr, "Remote Joy SDL for PSP (c) TyRaNiD 2k6\n"); + fprintf(stderr, "Built %s %s - $Revision: 2398 $\n", __DATE__, __TIME__); + memset(&g_context, 0, sizeof(g_context)); + if(parse_args(argc, argv, &g_context.args)) + { + mainloop(); + } + else + { + print_help(); + } + + return 0; +} diff --git a/tools/remotejoy/pcsdl/remotejoy.o b/tools/remotejoy/pcsdl/remotejoy.o new file mode 100644 index 0000000..68d588c Binary files /dev/null and b/tools/remotejoy/pcsdl/remotejoy.o differ diff --git a/tools/remotejoy/pcsdl/sidewinder.map b/tools/remotejoy/pcsdl/sidewinder.map new file mode 100644 index 0000000..db77620 --- /dev/null +++ b/tools/remotejoy/pcsdl/sidewinder.map @@ -0,0 +1,17 @@ +# Mapping file for Sidewinder Gamepad USB + +# Specify the digital axis +digital:0 + +# Specify the analog joystick +# analog:0 + +# Specify the button mappings +square:4 +triangle:5 +cross:1 +circle:2 +ltrig:6 +rtrig:7 +start:9 +select:8 diff --git a/tools/remotejoy/pcsdl/xpad.map b/tools/remotejoy/pcsdl/xpad.map new file mode 100644 index 0000000..807280e --- /dev/null +++ b/tools/remotejoy/pcsdl/xpad.map @@ -0,0 +1,11 @@ +tol:10000 +digital:3 +cross:0 +circle:1 +triangle:4 +square:3 +ltrig:5 +rtrig:2 +start:6 +select:9 +analog:0 diff --git a/tools/remotejoy/remotejoy.h b/tools/remotejoy/remotejoy.h new file mode 100644 index 0000000..b7003cf --- /dev/null +++ b/tools/remotejoy/remotejoy.h @@ -0,0 +1,46 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * remotejoy.h - PSPLINK PC remote joystick handler + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/tools/remotejoy/remotejoy.h $ + * $Id: remotejoy.h 2186 2007-02-20 04:09:44Z tyranid $ + */ +#ifndef __REMOTE_JOY__ +#define __REMOTE_JOY__ + +#define JOY_MAGIC 0x909ACCEF + +#define TYPE_BUTTON_DOWN 1 +#define TYPE_BUTTON_UP 2 +#define TYPE_ANALOG_Y 3 +#define TYPE_ANALOG_X 4 +#define TYPE_SCREEN_CMD 5 + +/* Screen commands */ +#define SCREEN_CMD_ACTIVE 1 +#define SCREEN_CMD_HSIZE 2 +#define SCREEN_CMD_FULLCOLOR 4 +#define SCREEN_CMD_DROPRATE(x) ((x)<<24) +#define SCREEN_CMD_GETDROPRATE(x) ((x)>>24) + +struct JoyEvent +{ + unsigned int magic; + int type; + unsigned int value; +} __attribute__((packed)); + +struct JoyScrHeader +{ + unsigned int magic; + int mode; /* 0-3 */ + int size; + int ref; +} __attribute__((packed)); + +#endif diff --git a/tools/scrkprintf/Makefile b/tools/scrkprintf/Makefile new file mode 100644 index 0000000..8f2e69f --- /dev/null +++ b/tools/scrkprintf/Makefile @@ -0,0 +1,20 @@ +TARGET = scrkprintf +OBJS = main.o + +# Use the kernel's small inbuilt libc +USE_KERNEL_LIBC = 1 +# Use only kernel libraries +USE_KERNEL_LIBS = 1 + +INCDIR = +#CFLAGS = -O2 -G0 -Wall -fno-builtin-printf -DDEBUG +CFLAGS = -Os -G0 -Wall -fno-builtin-printf +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = + +LIBS = -lpspdebug -lpspge_driver + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak diff --git a/tools/scrkprintf/README b/tools/scrkprintf/README new file mode 100644 index 0000000..0826474 --- /dev/null +++ b/tools/scrkprintf/README @@ -0,0 +1,17 @@ +PSPLINK USB Kprintf driver (c) TyRaNiD 2k6 + +This is a simple tool to output kprintf via USB (or for that matter wifi) which by default isn't possible. +Kprintf is designed to be called from virtually anywhere, with interrupts enabled or disabled so in the +usual case you cannot just pass the characters to printf and expect them to work as they probably wont, +if you are lucky you get away with it but most of the time it will probably crash the PSP. This app works +by throwing the characters into a buffer and signalling a thread to print the data set. There is a small +risk of an infinite loop occuring if the event flag screws up but hopefully that wont happen very often :P + +To use ensure kprintf=1 in the psplink.ini file and then run usbkprintf.prx. Kprintf should now go via +the normal printf channel. + +There are a couple of bugs/limitations with this app, if the 4k buffer fills up kprintf data will be lost, +you can see this if you run memblocks in the shell for example. And there is the aformentioned risk of +an infinite loop, most certainly do not run with a debug build of usbhostfs unless you like holding the +power button. Still it should be good enough to capture the errors from module loads etc which are sometimes +very useful :) diff --git a/tools/scrkprintf/exports.exp b/tools/scrkprintf/exports.exp new file mode 100644 index 0000000..b7940e8 --- /dev/null +++ b/tools/scrkprintf/exports.exp @@ -0,0 +1,12 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/tools/scrkprintf/main.c b/tools/scrkprintf/main.c new file mode 100644 index 0000000..d445fce --- /dev/null +++ b/tools/scrkprintf/main.c @@ -0,0 +1,47 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK Screen Kprintf driver + * + * Copyright (c) 2007 James F + * + * $HeadURL$ + * $Id$ + */ +#include +#include +#include +#include +#include +#include +#include + +PSP_MODULE_INFO("ScrKprintf", PSP_MODULE_KERNEL, 1, 1); + +static void PutCharDebug(unsigned short *data, unsigned int type) +{ + if((type & 0xFF00) == 0) + { + if((type == '\n') || (type == '\r') || (type >= 32)) + { + pspDebugScreenPrintData((char*) &type, 1); + } + } +} + +/* Entry point */ +int module_start(SceSize args, void *argp) +{ + pspDebugScreenInit(); + sceKernelRegisterDebugPutchar(PutCharDebug); + + return 0; +} + +/* Module stop entry */ +int module_stop(SceSize args, void *argp) +{ + return 0; +} diff --git a/tools/siokprintf/Makefile b/tools/siokprintf/Makefile new file mode 100644 index 0000000..0c19ee4 --- /dev/null +++ b/tools/siokprintf/Makefile @@ -0,0 +1,22 @@ +TARGET = siokprintf +OBJS = main.o + +# Use the kernel's small inbuilt libc +USE_KERNEL_LIBC = 1 +# Use only kernel libraries +USE_KERNEL_LIBS = 1 + +INCDIR = +#CFLAGS = -O2 -G0 -Wall -fno-builtin-printf -DDEBUG +CFLAGS = -Os -G0 -Wall -fno-builtin-printf +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = + +LIBS = + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak + +LIBS += -lpsphprm_driver diff --git a/tools/siokprintf/exports.exp b/tools/siokprintf/exports.exp new file mode 100644 index 0000000..b7940e8 --- /dev/null +++ b/tools/siokprintf/exports.exp @@ -0,0 +1,12 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/tools/siokprintf/main.c b/tools/siokprintf/main.c new file mode 100644 index 0000000..84a9bdb --- /dev/null +++ b/tools/siokprintf/main.c @@ -0,0 +1,120 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK SIO Kprintf driver + * + * Copyright (c) 2007 James F + * + * $HeadURL$ + * $Id$ + */ +#include +#include +#include +#include +#include +#include +#include + +PSP_MODULE_INFO("SioKprintf", PSP_MODULE_KERNEL, 1, 1); + +#define DEFAULT_BAUD 115200 + +/* Define some important parameters, not really sure on names. Probably doesn't matter */ +#define PSP_UART4_FIFO 0xBE500000 +#define PSP_UART4_STAT 0xBE500018 +#define PSP_UART4_DIV1 0xBE500024 +#define PSP_UART4_DIV2 0xBE500028 +#define PSP_UART4_CTRL 0xBE50002C +#define PSP_UART_CLK 96000000 +#define PSP_UART_TXFULL 0x20 +#define PSP_UART_RXEMPTY 0x10 + +/* Some function prototypes we will need */ +int sceHprmEnd(void); +int sceSysregUartIoEnable(int uart); +int sceSysconCtrlHRPower(int power); + +void sioPutchar(int ch) +{ + while(_lw(PSP_UART4_STAT) & PSP_UART_TXFULL); + _sw(ch, PSP_UART4_FIFO); +} + +void sioSetBaud(int baud) +{ + int div1, div2; + + /* rate set using the rough formula div1 = (PSP_UART_CLK / baud) >> 6 and + * div2 = (PSP_UART_CLK / baud) & 0x3F + * The uart4 driver actually uses a slightly different formula for div 2 (it + * adds 32 before doing the AND, but it doesn't seem to make a difference + */ + div1 = PSP_UART_CLK / baud; + div2 = div1 & 0x3F; + div1 >>= 6; + + _sw(div1, PSP_UART4_DIV1); + _sw(div2, PSP_UART4_DIV2); + _sw(0x60, PSP_UART4_CTRL); +} + +static void _sioInit(void) +{ + /* Shut down the remote driver */ + sceHprmEnd(); + /* Enable UART 4 */ + sceSysregUartIoEnable(4); + /* Enable remote control power */ + sceSysconCtrlHRPower(1); + /* Delay briefly */ + sceKernelDelayThread(2000000); +} + +static void PutCharDebug(unsigned short *data, unsigned int type) +{ + if((type & 0xFF00) == 0) + { + if(type == '\n') + { + sioPutchar('\r'); + } + + sioPutchar(type); + } +} + +int main_thread(SceSize args, void *argp) +{ + _sioInit(); + sioSetBaud(115200); + + sceKernelRegisterDebugPutchar(PutCharDebug); + + sceKernelExitDeleteThread(0); + + return 0; +} + +/* Entry point */ +int module_start(SceSize args, void *argp) +{ + int thid; + + /* Create a high priority thread */ + thid = sceKernelCreateThread("SioKprintf", main_thread, 7, 0x800, 0, NULL); + if(thid >= 0) + { + sceKernelStartThread(thid, args, argp); + } + + return 0; +} + +/* Module stop entry */ +int module_stop(SceSize args, void *argp) +{ + return 0; +} diff --git a/tools/usbkprintf/Makefile b/tools/usbkprintf/Makefile new file mode 100644 index 0000000..eee8fc7 --- /dev/null +++ b/tools/usbkprintf/Makefile @@ -0,0 +1,20 @@ +TARGET = usbkprintf +OBJS = main.o + +# Use the kernel's small inbuilt libc +USE_KERNEL_LIBC = 1 +# Use only kernel libraries +USE_KERNEL_LIBS = 1 + +INCDIR = +#CFLAGS = -O2 -G0 -Wall -fno-builtin-printf -DDEBUG +CFLAGS = -Os -G0 -Wall -fno-builtin-printf +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = + +LIBS = + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak diff --git a/tools/usbkprintf/README b/tools/usbkprintf/README new file mode 100644 index 0000000..0826474 --- /dev/null +++ b/tools/usbkprintf/README @@ -0,0 +1,17 @@ +PSPLINK USB Kprintf driver (c) TyRaNiD 2k6 + +This is a simple tool to output kprintf via USB (or for that matter wifi) which by default isn't possible. +Kprintf is designed to be called from virtually anywhere, with interrupts enabled or disabled so in the +usual case you cannot just pass the characters to printf and expect them to work as they probably wont, +if you are lucky you get away with it but most of the time it will probably crash the PSP. This app works +by throwing the characters into a buffer and signalling a thread to print the data set. There is a small +risk of an infinite loop occuring if the event flag screws up but hopefully that wont happen very often :P + +To use ensure kprintf=1 in the psplink.ini file and then run usbkprintf.prx. Kprintf should now go via +the normal printf channel. + +There are a couple of bugs/limitations with this app, if the 4k buffer fills up kprintf data will be lost, +you can see this if you run memblocks in the shell for example. And there is the aformentioned risk of +an infinite loop, most certainly do not run with a debug build of usbhostfs unless you like holding the +power button. Still it should be good enough to capture the errors from module loads etc which are sometimes +very useful :) diff --git a/tools/usbkprintf/exports.exp b/tools/usbkprintf/exports.exp new file mode 100644 index 0000000..b7940e8 --- /dev/null +++ b/tools/usbkprintf/exports.exp @@ -0,0 +1,12 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/tools/usbkprintf/main.c b/tools/usbkprintf/main.c new file mode 100644 index 0000000..4a091a4 --- /dev/null +++ b/tools/usbkprintf/main.c @@ -0,0 +1,153 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK USB Kprintf driver + * + * Copyright (c) 2006 James F + * + * $HeadURL$ + * $Id$ + */ +#include +#include +#include +#include +#include +#include +#include + +PSP_MODULE_INFO("UsbKprintf", PSP_MODULE_KERNEL, 1, 1); + +#define KPRINTF_BUF_SIZE 4096 +#define KPRINTF_EVENT 1 + +SceUID g_eventflag; +unsigned char g_buf[KPRINTF_BUF_SIZE]; +int g_writepos; +int g_readpos; +int g_total; + +static int add_char(unsigned char ch) +{ + int intc; + int total; + + intc = pspSdkDisableInterrupts(); + if(g_total < KPRINTF_BUF_SIZE) + { + g_buf[g_writepos++] = ch; + g_writepos %= KPRINTF_BUF_SIZE; + g_total++; + } + total = g_total; + pspSdkEnableInterrupts(intc); + + return total; +} + +static void PutCharDebug(unsigned short *data, unsigned int type) +{ + int total = 0; + + if((type & 0xFF00) == 0) + { + if((type == '\n') || (type == '\r') || (type >= 32)) + { + total = add_char((unsigned char) type); + } + } + + /* If we receive end of string or the buffer is full signal */ + if((type == 0x201) || (total == KPRINTF_BUF_SIZE)) + { + /* Set event flag */ + sceKernelSetEventFlag(g_eventflag, KPRINTF_EVENT); + } +} + +int main_thread(SceSize args, void *argp) +{ + int ret; + int intc; + unsigned char *plow, *phigh; + int low, high; + + memset(g_buf, 0, KPRINTF_BUF_SIZE); + g_writepos = 0; + g_readpos = 0; + g_total = 0; + plow = NULL; + phigh = NULL; + + g_eventflag = sceKernelCreateEventFlag("UsbKprintfEvent", 0, 0, 0); + if(g_eventflag < 0) + { + printf("Error creating event flag %08X\n", g_eventflag); + } + + sceKernelRegisterDebugPutchar(PutCharDebug); + + while(1) + { + ret = sceKernelWaitEventFlag(g_eventflag, KPRINTF_EVENT, 0x21, 0, 0); + if(ret < 0) + { + sceKernelExitDeleteThread(0); + } + + low = 0; + high = 0; + + intc = pspSdkDisableInterrupts(); + if(g_total > 0) + { + plow = &g_buf[g_readpos]; + low = (KPRINTF_BUF_SIZE - g_readpos) < g_total ? (KPRINTF_BUF_SIZE - g_readpos) : g_total ; + if(low < g_total) + { + phigh = g_buf; + high = g_total - low; + } + } + pspSdkEnableInterrupts(intc); + + if((low != 0) && (high == 0)) + { + printf("%.*s", low, plow); + } + else if((low != 0) && (high != 0)) + { + printf("%.*s%.*s", low, plow, high, phigh); + } + + intc = pspSdkDisableInterrupts(); + g_total -= (low+high); + g_readpos += (low+high); + g_readpos %= KPRINTF_BUF_SIZE; + pspSdkEnableInterrupts(intc); + } + + return 0; +} + +/* Entry point */ +int module_start(SceSize args, void *argp) +{ + int thid; + + /* Create a high priority thread */ + thid = sceKernelCreateThread("UsbKprintf", main_thread, 7, 0x800, 0, NULL); + if(thid >= 0) + { + sceKernelStartThread(thid, args, argp); + } + return 0; +} + +/* Module stop entry */ +int module_stop(SceSize args, void *argp) +{ + return 0; +} diff --git a/tools/webget/Makefile b/tools/webget/Makefile new file mode 100644 index 0000000..88c3a36 --- /dev/null +++ b/tools/webget/Makefile @@ -0,0 +1,16 @@ +TARGET = webget +OBJS = webget.o + +BUILD_PRX=1 + +INCDIR = +CFLAGS = -O2 -G0 -Wall +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = +LDFLAGS = +LIBS=-lcurl -lz + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build.mak diff --git a/tools/webget/README b/tools/webget/README new file mode 100644 index 0000000..6e2a359 --- /dev/null +++ b/tools/webget/README @@ -0,0 +1,2 @@ +Simple wget like clone using libcurl, allows you to download files to +the ms without needing USB (well you still need wifi and a web/ftp server :P) diff --git a/tools/webget/webget.c b/tools/webget/webget.c new file mode 100644 index 0000000..a89716d --- /dev/null +++ b/tools/webget/webget.c @@ -0,0 +1,151 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * webget.c - Simple wget like clone using libcurl + * + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/tools/webget/webget.c $ + * $Id: webget.c 1910 2006-05-14 18:39:19Z tyranid $ + */ +#include +#include +#include +#include + +/* Define the module info section */ +PSP_MODULE_INFO("webget", 0, 1, 1); +PSP_MAIN_THREAD_NAME("WebGet"); + +/* Define the main thread's attribute value (optional) */ +PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER); + +#define WRITEBUF_SIZE (512*1024) + +unsigned char g_writebuf[WRITEBUF_SIZE]; +int g_writebufpos = 0; + +/* Buffer up the data */ +size_t write_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + int *fd = (int *) data; + int totalsize = size * nmemb; + int ret = totalsize; + + if((totalsize + g_writebufpos) < WRITEBUF_SIZE) + { + memcpy(&g_writebuf[g_writebufpos], ptr, totalsize); + g_writebufpos += totalsize; + } + else + { + if(g_writebufpos > 0) + { + sceIoWrite(*fd, g_writebuf, g_writebufpos); + g_writebufpos = 0; + } + + while(totalsize > WRITEBUF_SIZE) + { + sceIoWrite(*fd, ptr, WRITEBUF_SIZE); + totalsize -= WRITEBUF_SIZE; + ptr += WRITEBUF_SIZE; + } + + if(totalsize > 0) + { + memcpy(g_writebuf, ptr, totalsize); + g_writebufpos = totalsize; + } + } + + return ret; +} + +int main(int argc, char *argv[]) +{ + int fd = -1; + CURL *curl = NULL; + double speed = 0.0; + double size = 0.0; + + printf("WebGet v0.1 (uses the CURL library)\n"); + if(argc < 3) + { + printf("Usage: webget.prx URL output\n"); + return 1; + } + + do + { + fd = sceIoOpen(argv[2], PSP_O_WRONLY | PSP_O_TRUNC | PSP_O_CREAT, 0777); + if(fd < 0) + { + printf("Couldn't open file %s, 0x%08X\n", argv[2], fd); + break; + } + + curl = curl_easy_init(); + if(curl == NULL) + { + printf("Couldn't initialise curl library\n"); + break; + } + + if(curl_easy_setopt(curl, CURLOPT_URL, argv[1]) != CURLE_OK) + { + printf("Could not set curl URL\n"); + break; + } + + if(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &fd) != CURLE_OK) + { + printf("Could not set write file pointer\n"); + break; + } + + if(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback) != CURLE_OK) + { + printf("Could not set write callback\n"); + break; + } + + if(curl_easy_perform(curl) != CURLE_OK) + { + printf("Could not read data from URL\n"); + break; + } + + if(g_writebufpos > 0) + { + sceIoWrite(fd, g_writebuf, g_writebufpos); + } + + if(curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &size) != CURLE_OK) + { + printf("Couldn't get the data size\n"); + } + + if(curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD, &speed) != CURLE_OK) + { + printf("Couldn't get the download speed\n"); + } + + printf("Download %d bytes, %fKb/s\n", (int) size, speed / 1024.0); + } + while(0); + + if(curl) + { + curl_easy_cleanup(curl); + } + + if(fd >= 0) + { + sceIoClose(fd); + } + + return 0; +} diff --git a/ttylink/Makefile b/ttylink/Makefile new file mode 100644 index 0000000..16cd2c1 --- /dev/null +++ b/ttylink/Makefile @@ -0,0 +1,13 @@ +OUTPUT=ttylink +OBJS=ttylink.o + +CFLAGS=-Wall -g +LIBS=-lreadline -lcurses + +all: ttylink + +$(OUTPUT): $(OBJS) + $(CC) -o $@ $^ $(LIBS) + +clean: + rm -f $(OUTPUT) *.o diff --git a/ttylink/ttylink.c b/ttylink/ttylink.c new file mode 100644 index 0000000..b46090b --- /dev/null +++ b/ttylink/ttylink.c @@ -0,0 +1,398 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * ttyline.c - PSPLINK text terminal + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/pcterm/pcterm.c $ + * $Id: pcterm.c 1963 2006-07-07 17:25:29Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SOL_TCP +#define SOL_TCP 6 +#endif + +#define DEFAULT_PORT 10000 +#define DEFAULT_IP "localhost" + +struct Args +{ + const char *ip; + const char *log; + unsigned short port; +}; + +struct GlobalContext +{ + struct Args args; + int exit; + int conn_sanity; + fd_set readsave; + int outsock; + int errsock; + int log; +}; + +struct GlobalContext g_context; + +int fixed_write(int s, const void *buf, int len) +{ + int written = 0; + + while(written < len) + { + int ret; + + ret = write(s, buf+written, len-written); + if(ret < 0) + { + if(errno != EINTR) + { + perror("write"); + written = -1; + break; + } + } + else + { + written += ret; + } + } + + return written; +} + +void cli_handler(char *buf) +{ + if((buf) && (*buf)) + { + write(g_context.outsock, buf, strlen(buf)); + write(g_context.outsock, "\n", 1); + } +} + +int init_readline(void) +{ + rl_callback_handler_install("", cli_handler); + + return 1; +} + +int parse_args(int argc, char **argv, struct Args *args) +{ + memset(args, 0, sizeof(*args)); + args->port = DEFAULT_PORT; + + while(1) + { + int ch; + int error = 0; + + ch = getopt(argc, argv, "p:l:"); + if(ch < 0) + { + break; + } + + switch(ch) + { + case 'p': args->port = atoi(optarg); + break; + case 'l': args->log = optarg; + break; + default : error = 1; + break; + }; + + if(error) + { + return 0; + } + } + + argc -= optind; + argv += optind; + + if(argc < 1) + { + args->ip = DEFAULT_IP; + } + else + { + args->ip = argv[0]; + } + + return 1; +} + +void print_help(void) +{ + fprintf(stderr, "TTYLink Help\n"); + fprintf(stderr, "Usage: ttyline [options] [ipaddr]\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "-p port : Specify the port number\n"); + fprintf(stderr, "-n : Do not connect up the tty (stdin/stdout/stderr)\n"); +} + +int init_sockaddr(struct sockaddr_in *name, const char *ipaddr, unsigned short port) +{ + struct hostent *hostinfo; + + name->sin_family = AF_INET; + name->sin_port = htons(port); + hostinfo = gethostbyname(ipaddr); + if(hostinfo == NULL) + { + fprintf(stderr, "Unknown host %s\n", ipaddr); + return 0; + } + name->sin_addr = *(struct in_addr *) hostinfo->h_addr; + + return 1; +} + +int read_outsocket(int sock) +{ + char buf[1024]; + int len; + + len = read(sock, buf, sizeof(buf)-1); + if(len < 0) + { + perror("read"); + return -1; + } + + /* EOF */ + if(len == 0) + { + return -1; + } + + buf[len] = 0; + + fprintf(stdout, "%s", buf); + fflush(stdout); + + return len; +} + +int read_errsocket(int sock) +{ + char buf[1024]; + int len; + + len = read(sock, buf, sizeof(buf)-1); + if(len < 0) + { + perror("read"); + return -1; + } + + /* EOF */ + if(len == 0) + { + return -1; + } + + buf[len] = 0; + + fprintf(stderr, "%s", buf); + fflush(stderr); + + return len; +} + +int connect_to(const char *ipaddr, unsigned short port) +{ + int sock = -1; + int success = 0; + struct sockaddr_in name; + + do + { + if(!init_sockaddr(&name, ipaddr, port)) + { + break; + } + sock = socket(PF_INET, SOCK_STREAM, 0); + if(sock < 0) + { + perror("socket"); + break; + } + + if(connect(sock, (struct sockaddr *) &name, sizeof(name)) < 0) + { + perror("connect"); + break; + } + else + { + FD_SET(sock, &g_context.readsave); + } + + success = 1; + } + while(0); + + if(!success) + { + if(sock >= 0) + { + close(sock); + sock = -1; + } + } + + return sock; +} + +void shell(void) +{ + fd_set readset; + FD_ZERO(&g_context.readsave); + + printf("Opening connection to %s port %d\n", g_context.args.ip, g_context.args.port); + + if((g_context.outsock = connect_to(g_context.args.ip, g_context.args.port+2)) < 0) + { + fprintf(stderr, "Could not connect to stdout channel\n"); + return; + } + if((g_context.errsock = connect_to(g_context.args.ip, g_context.args.port+3)) < 0) + { + fprintf(stderr, "Could not connect to stderr channel\n"); + return; + } + + init_readline(); + + FD_SET(STDIN_FILENO, &g_context.readsave); + + while(!g_context.exit) + { + int ret; + + readset = g_context.readsave; + ret = select(FD_SETSIZE, &readset, NULL, NULL, NULL); + if(ret < 0) + { + if(errno == EINTR) + { + continue; + } + + perror("select"); + break; + } + else if(ret == 0) + { + continue; + } + else + { + if(FD_ISSET(STDIN_FILENO, &readset)) + { + rl_callback_read_char(); + } + + if(FD_ISSET(g_context.outsock, &readset)) + { + if(read_outsocket(g_context.outsock) < 0) + { + FD_CLR(g_context.outsock, &g_context.readsave); + close(g_context.outsock); + g_context.outsock = -1; + } + } + if(FD_ISSET(g_context.errsock, &readset)) + { + if(read_errsocket(g_context.errsock) < 0) + { + FD_CLR(g_context.errsock, &g_context.readsave); + close(g_context.errsock); + g_context.errsock = -1; + } + } + } + } + + rl_callback_handler_remove(); +} + +void sig_call(int sig) +{ + if((sig == SIGINT) || (sig == SIGTERM)) + { + printf("Exiting\n"); + if(g_context.outsock >= 0) + { + close(g_context.outsock); + g_context.outsock = -1; + } + if(g_context.errsock >= 0) + { + close(g_context.errsock); + g_context.errsock = -1; + } + rl_callback_handler_remove(); + exit(0); + } +} + +int main(int argc, char **argv) +{ + memset(&g_context, 0, sizeof(g_context)); + g_context.outsock = -1; + g_context.errsock = -1; + g_context.log = -1; + if(parse_args(argc, argv, &g_context.args)) + { + if(g_context.args.log) + { + g_context.log = open(g_context.args.log, O_WRONLY | O_CREAT | O_TRUNC, 0660); + if(g_context.log < 0) + { + fprintf(stderr, "Warning: Could not open log file %s (%s)\n", g_context.args.log, + strerror(errno)); + } + } + shell(); + if(g_context.outsock >= 0) + { + close(g_context.outsock); + } + if(g_context.errsock >= 0) + { + close(g_context.errsock); + } + if(g_context.log >= 0) + { + close(g_context.log); + } + } + else + { + print_help(); + } + + return 0; +} diff --git a/usbgdb/Makefile b/usbgdb/Makefile new file mode 100644 index 0000000..d2954d8 --- /dev/null +++ b/usbgdb/Makefile @@ -0,0 +1,21 @@ +TARGET = usbgdb +OBJS = main.o gdb-stub.o exports.o + +# Use the kernel's small inbuilt libc +USE_KERNEL_LIBC = 1 +# Use only kernel libraries +USE_KERNEL_LIBS = 1 + +INCDIR = +#CFLAGS = -O2 -G0 -Wall -fno-builtin-printf -DDEBUG +CFLAGS = -Os -G0 -Wall -fno-builtin-printf -I../usbhostfs -I../psplink +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) +LDFLAGS = -L../libpsplink_driver -L../libusbhostfs_driver + +LIBDIR = + +LIBS = -lpsplink_driver -lusbhostfs_driver + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak diff --git a/usbgdb/common.h b/usbgdb/common.h new file mode 100644 index 0000000..3a81b4b --- /dev/null +++ b/usbgdb/common.h @@ -0,0 +1,86 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * common.h - GDB stub for psplink + * + * Copyright (c) 2007 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/usbgdb/common.h $ + * $Id: common.h 2174 2007-02-08 19:49:27Z tyranid $ + */ +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include +#include + +struct GdbContext +{ + SceSize args; + void *argp; + SceUID uid; + SceKernelModuleInfo info; + int started; + struct PsplinkContext ctx; + int elf; +}; + +extern struct GdbContext g_context; + +/* Define ELF types */ +typedef u32 Elf32_Addr; +typedef u16 Elf32_Half; +typedef u32 Elf32_Off; +typedef s32 Elf32_Sword; +typedef u32 Elf32_Word; + +#define ELF_MAGIC 0x464C457F + +#define ELF_MIPS_TYPE 0x0002 +#define ELF_PRX_TYPE 0xFFA0 + +/* ELF file header */ +typedef struct { + Elf32_Word e_magic; + u8 e_class; + u8 e_data; + u8 e_idver; + u8 e_pad[9]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} __attribute__((packed)) Elf32_Ehdr; + +int GdbReadByte(unsigned char *address, unsigned char *dest); +int GdbWriteByte(char val, unsigned char *dest); +int GdbHandleException (struct PsplinkContext *ctx); +int putDebugChar(unsigned char ch); +int getDebugChar(unsigned char *ch); +int peekDebugChar(unsigned char *ch); +int writeDebugData(void *data, int len); + +//#define DEBUG + +#ifdef DEBUG_PRINTF +#undef DEBUG_PRINTF +#endif + +#ifdef DEBUG +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +#endif diff --git a/usbgdb/exports.exp b/usbgdb/exports.exp new file mode 100644 index 0000000..27e0177 --- /dev/null +++ b/usbgdb/exports.exp @@ -0,0 +1,11 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_FUNC(module_reboot_before) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/usbgdb/gdb-stub.c b/usbgdb/gdb-stub.c new file mode 100644 index 0000000..d1cdc93 --- /dev/null +++ b/usbgdb/gdb-stub.c @@ -0,0 +1,762 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * gdb-stub.c - GDB stub for psplink + * + * Copyright (c) 2005 Julian T + * Copyright (c) 2005 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/usbgdb/gdb-stub.c $ + * $Id: gdb-stub.c 2179 2007-02-15 17:38:02Z tyranid $ + */ +/* Note: there is the odd small bit which comes from the gdb stubs/linux mips stub */ +/* As far as I am aware they didn't have an explicit license on them so... */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +#define MAX_BUF 2048 + +void psplinkReset(void); + +static char input[MAX_BUF]; +static char output[MAX_BUF]; +static const char hexchars[]="0123456789abcdef"; + +/* + * send the packet in buffer. + */ +static int putpacket(unsigned char *buffer) +{ + static unsigned char outputbuffer[4096]; + unsigned char checksum; + int count; + unsigned char ch; + int i = 0; + + /* + * $#. + */ + + do { + outputbuffer[i++] = '$'; + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + checksum += ch; + count += 1; + outputbuffer[i++] = ch; + } + + outputbuffer[i++] = '#'; + + outputbuffer[i++] = hexchars[(checksum >> 4) & 0xf]; + + outputbuffer[i++] = hexchars[checksum & 0xf]; + + DEBUG_PRINTF("Writing %.*s\n", i, outputbuffer); + writeDebugData(outputbuffer, i); + + DEBUG_PRINTF("calculated checksum = %02X\n", checksum); + + if(getDebugChar(&ch) <= 0) + { + return 0; + } + } + while ((ch & 0x7f) != '+'); + + return 1; +} + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* + * scan for the sequence $# + */ +static int getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* + * wait around for the start character, + * ignore all other characters + */ + ch = 0; + while((ch & 0x7F) != '$') + { + if(getDebugChar(&ch) <= 0) + { + return 0; + } + } + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < MAX_BUF) { + if(getDebugChar(&ch) <= 0) + { + return 0; + } + + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= MAX_BUF) + continue; + + buffer[count] = 0; + + if (ch == '#') { + if(getDebugChar(&ch) <= 0) + { + return 0; + } + xmitcsum = hex(ch & 0x7f) << 4; + if(getDebugChar(&ch) <= 0) + { + return 0; + } + xmitcsum |= hex(ch & 0x7f); + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); + + return 1; +} + +static struct hard_trap_info { + unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 6, SIGBUS }, /* instruction bus error */ + { 7, SIGBUS }, /* data bus error */ + { 9, SIGTRAP }, /* break */ + { 10, SIGILL }, /* reserved instruction */ + { 12, SIGFPE }, /* overflow */ + { 13, SIGTRAP }, /* trap */ + { 14, SIGSEGV }, /* virtual instruction cache coherency */ + { 15, SIGFPE }, /* floating point exception */ + { 23, SIGSEGV }, /* watch */ + { 31, SIGSEGV }, /* virtual data cache coherency */ + { 0, 0} /* Must be last */ +}; + +static int computeSignal(int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +static char *mem2hex(unsigned char *mem, char *buf, int count) +{ + unsigned char ch; + + while (count-- > 0) { + if (GdbReadByte(mem++, &ch) == 0) + { + return NULL; + } + + *buf++ = hexchars[(ch >> 4) & 0xf]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + + return buf; +} + +static char *hex2mem(char *buf, char *mem, int count, int binary) +{ + int i; + unsigned char ch; + + for (i=0; i> 4]; + *ptr++ = hexchars[*str & 0xf]; + str++; + } + *ptr = 0; + + return ptr; +} + +void build_trap_cmd(int sigval, struct PsplinkContext *ctx) +{ + char *ptr; + /* + * reply to host that an exception has occurred + */ + ptr = output; + *ptr++ = 'T'; + *ptr++ = hexchars[(sigval >> 4) & 0xf]; + *ptr++ = hexchars[sigval & 0xf]; + + /* + * Send Error PC + */ + *ptr++ = hexchars[37 >> 4]; + *ptr++ = hexchars[37 & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((unsigned char *) &ctx->regs.epc, ptr, sizeof(u32)); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + *ptr++ = hexchars[30 >> 4]; + *ptr++ = hexchars[30 & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((unsigned char *)&ctx->regs.r[30], ptr, sizeof(u32)); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + *ptr++ = hexchars[29 >> 4]; + *ptr++ = hexchars[29 & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((unsigned char *)&ctx->regs.r[29], ptr, sizeof(u32)); + *ptr++ = ';'; + + sprintf(ptr, "thread:%08x;", ctx->thid); + ptr += strlen(ptr); + + *ptr++ = 0; +} + +static void handle_query(struct PsplinkContext *ctx, char *str) +{ + static SceUID threads[20]; + static int thread_count = 0; + static int thread_loc = 0; + + switch(str[0]) + { + case 'C': sprintf(output, "QC%08X", ctx->thid); + break; + case 'f': + if(strncmp(str, "fThreadInfo", strlen("fThreadInfo")) == 0) + { + thread_count = psplinkReferThreadsByModule(SCE_KERNEL_TMID_Thread, g_context.uid, threads, 20); + + if(thread_count > 0) + { + thread_loc = 0; + sprintf(output, "m%08X", threads[thread_loc]); + thread_loc++; + } + } + break; + + case 's': + if(strncmp(str, "sThreadInfo", strlen("sThreadInfo")) == 0) + { + if(thread_loc < thread_count) + { + sprintf(output, "m%08X", threads[thread_loc]); + thread_loc++; + } + else + { + strcpy(output, "l"); + } + } + break; + case 'T': if(strncmp(str, "ThreadExtraInfo,", strlen("ThreadExtraInfo,")) == 0) + { + SceKernelThreadInfo info; + SceUID thid; + int i; + + str += strlen("ThreadExtraInfo,"); + if(hexToInt(&str, (unsigned int *) &thid)) + { + memset(&info, 0, sizeof(info)); + info.size = sizeof(info); + + i = sceKernelReferThreadStatus(thid, &info); + if(i == 0) + { + strtohex(output, info.name); + } + else + { + char temp[32]; + sprintf(temp, "Error: 0x%08X", i); + strtohex(output, temp); + } + } + } + break; + case 'P': + break; + case 'O': if(strncmp(str, "Offsets", strlen("Offsets")) == 0) + { + if(!g_context.elf) + { + u32 text; + + text = g_context.info.text_addr; + + sprintf(output, "Text=%08X;Data=%08X;Bss=%08X", text, text, text); + } + } + break; + }; +} + +void handle_bp(char *str, int set) +{ + char *ptr; + unsigned int addr; + unsigned int len; + int flags = 0; + + if((!isdigit(str[0])) || (str[1] != ',')) + { + DEBUG_PRINTF("Invalid Z string (%s)\n", str); + strcpy(output, "E01"); + return; + } + + ptr = &str[2]; + + if (hexToInt(&ptr, &addr) && *ptr++ == ',' && hexToInt(&ptr, &len)) + { + DEBUG_PRINTF("%c%c: addr 0x%08X, len 0x%08X\n", set ? 'Z' : 'z', str[0], addr, len); + switch(str[0]) + { + case '1': flags = DEBUG_BP_HARDWARE; + case '0': + if(set) + { + if(debugSetBP(addr, flags, 0)) + { + strcpy(output, "OK"); + } + else + { + strcpy(output, "E03"); + } + } + else + { + if(debugDeleteBP(addr)) + { + strcpy(output, "OK"); + } + else + { + strcpy(output, "EO3"); + } + } + break; + default: break; + }; + } + else + { + strcpy(output,"E01"); + } +} + +void suspend_module(void) +{ + int intc; + int count; + int i; + SceUID thids[20]; + + usbLockBus(); + intc = pspSdkDisableInterrupts(); + count = psplinkReferThreadsByModule(SCE_KERNEL_TMID_Thread, g_context.uid, thids, 20); + for(i = 0; i < count; i++) + { + sceKernelSuspendThread(thids[i]); + } + pspSdkEnableInterrupts(intc); + usbUnlockBus(); +} + +void resume_module(void) +{ + int intc; + int count; + int i; + SceUID thids[20]; + + usbLockBus(); + intc = pspSdkDisableInterrupts(); + count = psplinkReferThreadsByModule(SCE_KERNEL_TMID_Thread, g_context.uid, thids, 20); + for(i = 0; i < count; i++) + { + sceKernelResumeThread(thids[i]); + } + pspSdkEnableInterrupts(intc); + usbUnlockBus(); +} + +int GdbHandleException (struct PsplinkContext *ctx) +{ + int ret = 1; + int trap; + int sigval; + unsigned int addr; + unsigned int length; + char *ptr; + int bflag = 0; + SceUID thid = 0; + + DEBUG_PRINTF("In GDB Handle Exception\n"); + suspend_module(); + + if(ctx->regs.type == PSPLINK_EXTYPE_DEBUG) + { + sigval = SIGTRAP; + } + else + { + trap = (ctx->regs.cause & 0x7c) >> 2; + sigval = computeSignal(trap); + } + + if(sigval == SIGHUP) + { + DEBUG_PRINTF("Trap %d\n", trap); + } + + if(g_context.started) + { + build_trap_cmd(sigval, ctx); + putpacket((unsigned char *) output); + } + + while(1) + { + if(!getpacket(input)) + { + ret = 0; + ctx->cont = PSP_EXCEPTION_EXIT; + goto restart; + } + + if((input[0] != 'X') && (input[0] != 'x')) + { + DEBUG_PRINTF("Received packet '%s'\n", input); + } + else + { + DEBUG_PRINTF("Received binary packet\n"); + } + + output[0] = 0; + + switch (input[0]) + { + case '?': + build_trap_cmd(sigval, ctx); + break; + + case 'c': + ptr = &input[1]; + if(!g_context.started) + { + int status; + + /* Ensure any pending memory is flushed before we start the module */ + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + sceKernelStartModule(g_context.uid, g_context.args, g_context.argp, &status, NULL); + + g_context.started = 1; + } + else + { + if (hexToInt(&ptr, &addr)) + { + ctx->regs.epc = addr; + } + } + + ctx->cont = PSP_EXCEPTION_CONTINUE; + goto restart; + break; + + case 'D': + putpacket((unsigned char *) output); + ctx->cont = PSP_EXCEPTION_CONTINUE; + ret = 0; + goto restart; + break; + + case 'g': + { + struct PsplinkContext thctx; + struct PsplinkContext *pCtx = NULL; + + if(thid && (thid != ctx->thid)) + { + if(psplinkGetFullThreadContext(thid, &thctx)) + { + pCtx = &thctx; + } + } + else + { + pCtx = ctx; + } + + if(pCtx) + { + ptr = output; + ptr = (char*) mem2hex((unsigned char *)&pCtx->regs.r[0], ptr, 32*sizeof(u32)); /* r0...r31 */ + ptr = (char*) mem2hex((unsigned char *)&pCtx->regs.status, ptr, 6*sizeof(u32)); /* cp0 */ + ptr = (char*) mem2hex((unsigned char *)&pCtx->regs.fpr[0], ptr, 32*sizeof(u32)); /* f0...31 */ + ptr = (char*) mem2hex((unsigned char *)&pCtx->regs.fsr, ptr, 2*sizeof(u32)); /* cp1 */ + ptr = (char*) mem2hex((unsigned char *)&pCtx->regs.frame_ptr, ptr, 2*sizeof(u32)); /* frp */ + ptr = (char*) mem2hex((unsigned char *)&pCtx->regs.index, ptr, 16*sizeof(u32)); /* cp0 */ + } + else + { + strcpy(output, "E03"); + } + } + break; + + case 'G': + if(thid && (thid == ctx->thid)) + { + ptr = &input[1]; + hex2mem(ptr, (char *)&ctx->regs.r[0], 32*sizeof(unsigned int), 0); + ptr += 32*(2*sizeof(unsigned int)); + hex2mem(ptr, (char *)&ctx->regs.status, 6*sizeof(unsigned int), 0); + ptr += 6*(2*sizeof(unsigned int)); + hex2mem(ptr, (char *)&ctx->regs.fpr[0], 32*sizeof(unsigned int), 0); + ptr += 32*(2*sizeof(unsigned int)); + hex2mem(ptr, (char *)&ctx->regs.fsr, 2*sizeof(unsigned int), 0); + ptr += 2*(2*sizeof(unsigned int)); + hex2mem(ptr, (char *)&ctx->regs.frame_ptr, 2*sizeof(unsigned int), 0); + ptr += 2*(2*sizeof(unsigned int)); + hex2mem(ptr, (char *)&ctx->regs.index, 16*sizeof(unsigned int), 0); + } + + /* We just ignore other threads */ + strcpy(output,"OK"); + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((unsigned char *)addr, output, length)) + break; + strcpy (output, "E03"); + } else + strcpy(output,"E01"); + break; + + /* + * XAA..AA,LLLL: Write LLLL escaped binary bytes at address AA.AA + */ + case 'X': + bflag = 1; + /* fall through */ + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK + */ + case 'M': + ptr = &input[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length, bflag)) + strcpy(output, "OK"); + else + strcpy(output, "E03"); + } + else + strcpy(output, "E02"); + bflag = 0; + break; + + case 's': ptr = &input[1]; + if (hexToInt(&ptr, &addr)) + { + ctx->regs.epc = addr; + } + + ctx->cont = PSP_EXCEPTION_STEP; + goto restart; + break; + + case 'H': if(input[1] == 'g') + { + ptr = &input[2]; + if(hexToInt(&ptr, &addr)) + { + thid = addr; + DEBUG_PRINTF("Setting thread to 0x%08X\n", thid); + strcpy(output, "OK"); + } + else + { + strcpy(output, "EO3"); + } + } + break; + case 'q': handle_query(ctx, &input[1]); + break; + + case 'Z': handle_bp(&input[1], 1); + break; + + case 'z': handle_bp(&input[1], 0); + break; + + /* + * kill the program; let us try to restart the machine + * Reset the whole machine. + */ + case 'k': + case 'r': + psplinkReset(); + break; + + default: + break; + } + /* + * reply to the request + */ + + putpacket((unsigned char *) output); + + } /* while */ + +restart: + /* Flush caches */ + sceKernelDcacheWritebackInvalidateAll(); + sceKernelIcacheInvalidateAll(); + resume_module(); + + return ret; +} diff --git a/usbgdb/main.c b/usbgdb/main.c new file mode 100644 index 0000000..a2c2067 --- /dev/null +++ b/usbgdb/main.c @@ -0,0 +1,361 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK GDB stub + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/usbgdb/main.c $ + * $Id: main.c 2179 2007-02-15 17:38:02Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +PSP_MODULE_INFO("GDBServer", PSP_MODULE_KERNEL, 1, 1); + +#define GDB_POLL_TIMEOUT 1000000 + +struct AsyncEndpoint g_endp; +DebugEventHandler g_handler; +struct GdbContext g_context; +SceUID g_thid = -1; + +int initialise(SceSize args, void *argp) +{ + int len; + int fd; + Elf32_Ehdr hdr; + + memset(&g_context, 0, sizeof(g_context)); + if(argp == NULL) + { + return 0; + } + + len = strlen((char*) argp)+1; + argp += len; + args -= len; + if(args <= 0) + { + return 0; + } + + g_context.argp = argp; + g_context.args = args; + + fd = sceIoOpen((char*) argp, PSP_O_RDONLY, 0777); + if(fd < 0) + { + printf("%s does not exist\n", (char*) argp); + return 0; + } + + len = sceIoRead(fd, &hdr, sizeof(hdr)); + + sceIoClose(fd); + if(len != sizeof(hdr)) + { + printf("Could not read in ELF header\n"); + return 0; + } + + if(hdr.e_magic != ELF_MAGIC) + { + printf("Invalid ELF magic\n"); + return 0; + } + + if(hdr.e_type == ELF_MIPS_TYPE) + { + g_context.elf = 1; + } + else if(hdr.e_type != ELF_PRX_TYPE) + { + printf("Invalid ELF type code\n"); + return 0; + } + + g_context.uid = sceKernelLoadModule(argp, 0, NULL); + sceIoClose(fd); + + if(g_context.uid < 0) + { + printf("Could not load module %s (0x%08X)\n", (char*) argp, g_context.uid); + return 0; + } + + + if(!psplinkReferModule(g_context.uid, &g_context.info)) + { + printf("Could not refer module info\n"); + return 0; + } + + g_context.ctx.regs.epc = g_context.info.entry_addr; + g_context.ctx.regs.cause = 9 << 2; + + printf("Loaded %s - UID 0x%08X, Entry 0x%08X\n", (char*)argp, g_context.uid, g_context.info.entry_addr); + + return 1; +} + +/* These should be changed on for different remote methods */ +int GdbReadByte(unsigned char *address, unsigned char *dest) +{ + u32 addr; + int nibble; + int valid = 0; + + addr = (u32) address; + nibble = addr >> 28; + addr &= 0x0FFFFFFF; + + if((addr >= 0x08800000) && (addr < 0x0A000000)) + { + if((nibble == 0) || (nibble == 4) || (nibble == 8) || (nibble == 10)) + { + valid = 1; + } + } + + if((addr >= 0x08000000) && (addr < 0x08400000)) + { + if((nibble == 8) || (nibble == 10)) + { + valid = 1; + } + } + + if(valid) + { + *dest = *address; + return 1; + } + + return 0; +} + +int GdbWriteByte(char val, unsigned char *dest) +{ + u32 addr; + int nibble; + int valid = 0; + + addr = (u32) dest; + nibble = addr >> 28; + addr &= 0x0FFFFFFF; + + if((addr >= 0x08800000) && (addr < 0x0A000000)) + { + if((nibble == 0) || (nibble == 4) || (nibble == 8) || (nibble == 10)) + { + valid = 1; + } + } + + if((addr >= 0x08000000) && (addr < 0x08400000)) + { + if((nibble == 8) || (nibble == 10)) + { + valid = 1; + } + } + + if(valid) + { + *dest = val; + return 1; + } + + return 0; +} + +int putDebugChar(unsigned char ch) +{ + return usbAsyncWrite(ASYNC_GDB, &ch, 1); +} + +int getDebugChar(unsigned char *ch) +{ + int ret = 0; + + *ch = 0; + + do + { + ret = usbAsyncRead(ASYNC_GDB, ch, 1); + } + while(ret < 1); + + return ret; +} + +int peekDebugChar(unsigned char *ch) +{ + int ret = 0; + int intc; + + *ch = 0; + intc = pspSdkDisableInterrupts(); + if(g_endp.size > 0) + { + *ch = g_endp.buffer[g_endp.read_pos]; + ret = 1; + } + pspSdkEnableInterrupts(intc); + + return ret; +} + +int writeDebugData(void *data, int len) +{ + return usbAsyncWrite(ASYNC_GDB, data, len); +} + +int main_thread(SceSize args, void *argp) +{ + struct PsplinkContext *ctx; + int ret; + SceUInt timeout; + SceUID thids[20]; + int count; + int intc; + + printf("PSPLink USB GDBServer (c) 2k7 TyRaNiD\n"); + if(!initialise(args, argp)) + { + printf("Usage: usbgdb.prx program [args]\n"); + sceKernelExitDeleteThread(0); + } + + if(usbAsyncRegister(ASYNC_GDB, &g_endp) < 0) + { + printf("Could not register GDB provider\n"); + sceKernelExitDeleteThread(0); + } + + usbWaitForConnect(); + memset(&g_handler, 0, sizeof(g_handler)); + g_handler.size = sizeof(g_handler); + g_handler.membase = g_context.info.text_addr; + g_handler.memtop = g_context.info.text_addr + g_context.info.text_size; + g_handler.mbox = sceKernelCreateMbx("GDBMbx", 0, NULL); + if(g_handler.mbox < 0) + { + printf("Could not create message box\n"); + sceKernelExitDeleteThread(0); + } + + if(debugRegisterEventHandler(&g_handler) < 0) + { + printf("Could not register event handler\n"); + sceKernelExitDeleteThread(0); + } + + if(GdbHandleException(&g_context.ctx)) + { + while(1) + { + timeout = GDB_POLL_TIMEOUT; + ret = debugWaitDebugEvent(&g_handler, &ctx, &timeout); + + if(ret == 0) + { + DEBUG_PRINTF("ctx %p, epc 0x%08X\n", ctx, ctx->regs.epc); + ret = GdbHandleException(ctx); + sceKernelWakeupThread(ctx->thid); + if(ret == 0) + { + break; + } + } + else if(ret == SCE_KERNEL_ERROR_WAIT_TIMEOUT) + { + unsigned char ch; + + if(peekDebugChar(&ch) && (ch == 3)) + { + DEBUG_PRINTF("Break Issued\n"); + intc = pspSdkDisableInterrupts(); + count = psplinkReferThreadsByModule(SCE_KERNEL_TMID_Thread, g_context.uid, thids, 20); + if(count > 0) + { + /* We just break the first thread */ + /* Could in theory break on the thread which we are interested in ? */ + debugBreakThread(thids[0]); + } + pspSdkEnableInterrupts(intc); + + /* Should have a fallback if it just wont stop + GdbHandleException(&g_context.ctx); + */ + } + continue; + } + else + { + printf("Error waiting for debug event 0x%08X\n", ret); + break; + } + } + } + + debugUnregisterEventHandler(&g_handler); + sceKernelExitDeleteThread(0); + + return 0; +} + +/* Entry point */ +int module_start(SceSize args, void *argp) +{ + /* Create a high priority thread */ + g_thid = sceKernelCreateThread("GDBServer", main_thread, 15, 0x4000, 0, NULL); + if(g_thid >= 0) + { + sceKernelStartThread(g_thid, args, argp); + } + + return 0; +} + +void stop_gdb(void) +{ + if(g_thid > 0) + { + sceKernelSuspendThread(g_thid); + } + + /* Cancel mbx receive, should then force the thread to exit */ + sceKernelCancelReceiveMbx(g_handler.mbox, NULL); +} + +/* Module stop entry */ +int module_stop(SceSize args, void *argp) +{ + stop_gdb(); + + return 0; +} + +int module_reboot_before(SceSize args, void *argp) +{ + stop_gdb(); + + return 0; +} diff --git a/usbhostfs/Makefile b/usbhostfs/Makefile new file mode 100644 index 0000000..9fa4214 --- /dev/null +++ b/usbhostfs/Makefile @@ -0,0 +1,20 @@ +TARGET = usbhostfs +OBJS = main.o host_driver.o kmode.o exports.o + +# Use the kernel's small inbuilt libc +USE_KERNEL_LIBC = 1 +# Use only kernel libraries +USE_KERNEL_LIBS = 1 + +INCDIR = +#CFLAGS = -O2 -G0 -Wall -fno-builtin-printf -DDEBUG +CFLAGS = -O2 -G0 -Wall -fno-builtin-printf +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = + +LIBS = -lpspusbbus_driver + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak diff --git a/usbhostfs/exports.exp b/usbhostfs/exports.exp new file mode 100644 index 0000000..846e7bb --- /dev/null +++ b/usbhostfs/exports.exp @@ -0,0 +1,38 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_EXPORT_START(USBHostFS_driver, 0, 0x0001) +PSP_EXPORT_FUNC(usbAsyncRegister) +PSP_EXPORT_FUNC(usbAsyncUnregister) +PSP_EXPORT_FUNC(usbAsyncRead) +PSP_EXPORT_FUNC(usbAsyncReadWithTimeout) +PSP_EXPORT_FUNC(usbAsyncWrite) +PSP_EXPORT_FUNC(usbAsyncFlush) +PSP_EXPORT_FUNC(usbWaitForConnect) +PSP_EXPORT_FUNC(usbWriteBulkData) +PSP_EXPORT_FUNC(usbLockBus) +PSP_EXPORT_FUNC(usbUnlockBus) +PSP_EXPORT_END + +PSP_EXPORT_START(USBHostFS, 0, 0x4001) +PSP_EXPORT_FUNC(usbAsyncRegister) +PSP_EXPORT_FUNC(usbAsyncUnregister) +PSP_EXPORT_FUNC(usbAsyncRead) +PSP_EXPORT_FUNC(usbAsyncReadWithTimeout) +PSP_EXPORT_FUNC(usbAsyncWrite) +PSP_EXPORT_FUNC(usbAsyncFlush) +PSP_EXPORT_FUNC(usbWaitForConnect) +PSP_EXPORT_FUNC(usbWriteBulkData) +PSP_EXPORT_FUNC(usbLockBus) +PSP_EXPORT_FUNC(usbUnlockBus) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/usbhostfs/host_driver.c b/usbhostfs/host_driver.c new file mode 100644 index 0000000..b907ec1 --- /dev/null +++ b/usbhostfs/host_driver.c @@ -0,0 +1,1054 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * host_driver.c - PSPLINK HostFS IO driver + * + * Copyright (c) 2006 James Forshaw + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/usbhostfs/host_driver.c $ + * $Id: host_driver.c 1954 2006-06-28 17:54:29Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include "usbhostfs.h" + +static int io_init(PspIoDrvArg *arg) +{ + /* Nothing to do */ + return 0; +} + +static int io_exit(PspIoDrvArg *arg) +{ + /* Nothing to do */ + return 0; +} + +static int io_open(PspIoDrvFileArg *arg, char *file, int mode, SceMode mask) +{ + int ret = -1; + struct HostFsOpenCmd cmd; + struct HostFsOpenResp resp; + + if(file == NULL) + { + MODPRINTF("Invalid file name (NULL)\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_OPEN; + cmd.cmd.extralen = strlen(file)+1; + cmd.mode = mode; + cmd.mask = mask; + cmd.fsnum = arg->fs_num; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), file, strlen(file)+1, NULL, 0)) + { + /* Set the resultant fid into the arg structure */ + if(resp.res >= 0) + { + arg->arg = (void *) (resp.res); + ret = 0; + } + else + { + ret = resp.res; + } + + DEBUG_PRINTF("Returned fid %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending open command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_close(PspIoDrvFileArg *arg) +{ + int ret = -1; + struct HostFsCloseCmd cmd; + struct HostFsCloseResp resp; + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_CLOSE; + cmd.cmd.extralen = 0; + cmd.fid = (int) (arg->arg); + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), NULL, 0, NULL, 0)) + { + ret = resp.res; + DEBUG_PRINTF("Returned res %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending close command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +int usb_read_data(int fd, void *data, int len) +{ + struct HostFsReadCmd cmd; + struct HostFsReadResp resp; + int blocks; + int residual; + int ret = 0; + + if(len < 0) + { + MODPRINTF("Invalid read length (%d)\n", len); + return -1; + } + + if(len == 0) + { + return 0; + } + + if(data == NULL) + { + MODPRINTF("NULL data pointer\n"); + return -1; + } + + blocks = len / HOSTFS_MAX_BLOCK; + residual = len % HOSTFS_MAX_BLOCK; + + while(blocks > 0) + { + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_READ; + cmd.cmd.extralen = 0; + cmd.len = HOSTFS_MAX_BLOCK; + cmd.fid = fd; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), NULL, 0, data, HOSTFS_MAX_BLOCK)) + { + DEBUG_PRINTF("Read: Returned result %d\n", resp.res); + if(resp.res > 0) + { + data += resp.res; + ret += resp.res; + } + else + { + /* If we had an error straight away */ + if((resp.res < 0) && (ret == 0)) + { + ret = resp.res; + } + + /* Otherwise just return how much we managed to read */ + break; + } + } + else + { + MODPRINTF("Error in sending read command\n"); + ret = -1; + break; + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + ret = -1; + break; + } + + blocks--; + } + + do + { + if((blocks == 0) && (residual > 0)) + { + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_READ; + cmd.cmd.extralen = 0; + cmd.len = residual; + cmd.fid = fd; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), NULL, 0, data, residual)) + { + DEBUG_PRINTF("Read: Returned result %d\n", resp.res); + if(resp.res > 0) + { + data += resp.res; + ret += resp.res; + } + else + { + /* If we had an error straight away */ + if((resp.res < 0) && (ret == 0)) + { + ret = resp.res; + } + + /* Otherwise just return how much we managed to read */ + break; + } + } + else + { + MODPRINTF("Error in sending read command\n"); + ret = -1; + break; + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + ret = -1; + break; + } + } + } + while(0); + + return ret; +} + +static int io_read(PspIoDrvFileArg *arg, char *data, int len) +{ + DEBUG_PRINTF("read: arg %p, data %p, len %d\n", arg, data, len); + + return usb_read_data((int) arg->arg, data, len); +} + +int usb_write_data(int fd, const void *data, int len) +{ + struct HostFsWriteCmd cmd; + struct HostFsWriteResp resp; + int blocks; + int residual; + int ret = 0; + + if(len < 0) + { + MODPRINTF("Invalid write length (%d)\n", len); + return -1; + } + + if(len == 0) + { + return 0; + } + + if(data == NULL) + { + MODPRINTF("NULL data pointer\n"); + return -1; + } + + + blocks = len / HOSTFS_MAX_BLOCK; + residual = len % HOSTFS_MAX_BLOCK; + + while(blocks > 0) + { + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_WRITE; + cmd.cmd.extralen = HOSTFS_MAX_BLOCK; + cmd.fid = fd; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), data, HOSTFS_MAX_BLOCK, NULL, 0)) + { + DEBUG_PRINTF("Write: Returned result %d\n", resp.res); + if(resp.res > 0) + { + data += resp.res; + ret += resp.res; + } + else + { + /* If we had an error straight away */ + if((resp.res < 0) && (ret == 0)) + { + ret = resp.res; + } + + /* Otherwise just return how much we managed to write */ + + break; + } + } + else + { + MODPRINTF("Error in sending write command\n"); + ret = -1; + break; + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + ret = -1; + break; + } + + blocks--; + } + + do + { + if((blocks == 0) && (residual > 0)) + { + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_WRITE; + cmd.cmd.extralen = residual; + cmd.fid = fd; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), data, residual, NULL, 0)) + { + DEBUG_PRINTF("Write: Returned result %d\n", resp.res); + if(resp.res > 0) + { + ret += resp.res; + } + else + { + /* If we had an error straight away */ + if((resp.res < 0) && (ret == 0)) + { + ret = resp.res; + } + + /* Otherwise just return how much we managed to write */ + break; + } + } + else + { + MODPRINTF("Error in sending write command\n"); + ret = -1; + break; + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + ret = -1; + break; + } + } + } + while(0); + + return ret; +} + +static int io_write(PspIoDrvFileArg *arg, const char *data, int len) +{ + DEBUG_PRINTF("write: arg %p, data %p, len %d\n", arg, data, len); + + return usb_write_data((int) arg->arg, data, len); +} + +static SceOff io_lseek(PspIoDrvFileArg *arg, SceOff ofs, int whence) +{ + SceOff ret = -1; + struct HostFsLseekCmd cmd; + struct HostFsLseekResp resp; + + DEBUG_PRINTF("lseek: ofs %d, whence %d\n", (int) ofs, whence); + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_LSEEK; + cmd.fid = (int) (arg->arg); + cmd.ofs = ofs; + cmd.whence = whence; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), NULL, 0, NULL, 0)) + { + if(resp.res >= 0) + { + ret = resp.ofs; + } + + DEBUG_PRINTF("Lseek returned res %d\n", ret); + } + else + { + MODPRINTF("Error in sending lseek command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_ioctl(PspIoDrvFileArg *arg, unsigned int cmdno, void *indata, int inlen, void *outdata, int outlen) +{ + /* Do nothing atm */ + int ret = -1; + struct HostFsIoctlCmd cmd; + struct HostFsIoctlResp resp; + + /* Ensure our lengths are zeroed */ + if(indata == NULL) + { + inlen = 0; + } + + if(outdata == NULL) + { + outlen = 0; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_IOCTL; + cmd.cmd.extralen = inlen; + cmd.cmdno = cmdno; + cmd.fid = (int) (arg->arg); + cmd.outlen = outlen; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), indata, inlen, outdata, outlen)) + { + ret = resp.res; + DEBUG_PRINTF("Returned res %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending ioctl command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_remove(PspIoDrvFileArg *arg, const char *name) +{ + int ret = -1; + struct HostFsRemoveCmd cmd; + struct HostFsRemoveResp resp; + + if(name == NULL) + { + MODPRINTF("Invalid file name (NULL)\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_REMOVE; + cmd.cmd.extralen = strlen(name)+1; + cmd.fsnum = arg->fs_num; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), name, strlen(name)+1, NULL, 0)) + { + ret = resp.res; + DEBUG_PRINTF("Returned res %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending remove command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_mkdir(PspIoDrvFileArg *arg, const char *name, SceMode mode) +{ + int ret = -1; + struct HostFsMkdirCmd cmd; + struct HostFsMkdirResp resp; + + if(name == NULL) + { + MODPRINTF("Invalid file name (NULL)\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_MKDIR; + cmd.cmd.extralen = strlen(name)+1; + cmd.mode = mode; + cmd.fsnum = arg->fs_num; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), name, strlen(name)+1, NULL, 0)) + { + ret = resp.res; + DEBUG_PRINTF("Returned res %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending mkdir command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_rmdir(PspIoDrvFileArg *arg, const char *name) +{ + int ret = -1; + struct HostFsRmdirCmd cmd; + struct HostFsRmdirResp resp; + + if(name == NULL) + { + MODPRINTF("Invalid file name (NULL)\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_RMDIR; + cmd.cmd.extralen = strlen(name)+1; + cmd.fsnum = arg->fs_num; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), name, strlen(name)+1, NULL, 0)) + { + ret = resp.res; + DEBUG_PRINTF("Returned res %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending rmdir command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_dopen(PspIoDrvFileArg *arg, const char *dir) +{ + int ret = -1; + struct HostFsDopenCmd cmd; + struct HostFsDopenResp resp; + + if(dir == NULL) + { + MODPRINTF("Invalid dir name (NULL)\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_DOPEN; + cmd.cmd.extralen = strlen(dir)+1; + cmd.fsnum = arg->fs_num; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), dir, strlen(dir)+1, NULL, 0)) + { + /* Set the resultant did into the arg structure */ + if(resp.res >= 0) + { + arg->arg = (void *) (resp.res); + ret = 0; + } + else + { + ret = resp.res; + } + + DEBUG_PRINTF("Returned did %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending dopen command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_dclose(PspIoDrvFileArg *arg) +{ + int ret = -1; + struct HostFsDcloseCmd cmd; + struct HostFsDcloseResp resp; + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_DCLOSE; + cmd.cmd.extralen = 0; + cmd.did = (int) (arg->arg); + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), NULL, 0, NULL, 0)) + { + ret = resp.res; + DEBUG_PRINTF("Returned result %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending dclose command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_dread(PspIoDrvFileArg *arg, SceIoDirent *dir) +{ + int ret = -1; + struct HostFsDreadCmd cmd; + struct HostFsDreadResp resp; + + if(dir == NULL) + { + MODPRINTF("Invalid dir pointer (NULL)\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_DREAD; + cmd.cmd.extralen = 0; + cmd.did = (int) (arg->arg); + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), NULL, 0, dir, sizeof(SceIoDirent))) + { + DEBUG_PRINTF("Dread: Returned result %d\n", resp.res); + ret = resp.res; + } + else + { + MODPRINTF("Error in sending read command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_getstat(PspIoDrvFileArg *arg, const char *file, SceIoStat *stat) +{ + int ret = -1; + struct HostFsGetstatCmd cmd; + struct HostFsGetstatResp resp; + + if(file == NULL) + { + MODPRINTF("Invalid file name (NULL)\n"); + return -1; + } + + if(stat == NULL) + { + MODPRINTF("Invalid stat pointer\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_GETSTAT; + cmd.cmd.extralen = strlen(file)+1; + cmd.fsnum = arg->fs_num; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), file, strlen(file)+1, stat, sizeof(SceIoStat))) + { + ret = resp.res; + DEBUG_PRINTF("Returned res %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending getstat command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_chstat(PspIoDrvFileArg *arg, const char *file, SceIoStat *stat, int bits) +{ + int ret = -1; + struct HostFsChstatCmd cmd; + struct HostFsChstatResp resp; + + if(file == NULL) + { + MODPRINTF("Invalid file name (NULL)\n"); + return -1; + } + + if(stat == NULL) + { + MODPRINTF("Invalid stat pointer (NULL)\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_CHSTAT; + cmd.cmd.extralen = strlen(file)+1; + cmd.bits = bits; + cmd.mode = stat->st_mode; + cmd.size = stat->st_size; + cmd.atime.year = stat->st_atime.year; + cmd.atime.month = stat->st_atime.month; + cmd.atime.day = stat->st_atime.day; + cmd.atime.hour = stat->st_atime.hour; + cmd.atime.minute = stat->st_mtime.minute; + cmd.atime.second = stat->st_mtime.second; + cmd.mtime.year = stat->st_mtime.year; + cmd.mtime.month = stat->st_mtime.month; + cmd.mtime.day = stat->st_mtime.day; + cmd.mtime.hour = stat->st_mtime.hour; + cmd.mtime.minute = stat->st_mtime.minute; + cmd.mtime.second = stat->st_mtime.second; + cmd.fsnum = arg->fs_num; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), file, strlen(file)+1, NULL, 0)) + { + /* Set the resultant did into the arg structure */ + ret = resp.res; + + DEBUG_PRINTF("Returned res %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending chstat command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_rename(PspIoDrvFileArg *arg, const char *oldname, const char *newname) +{ + char buf[HOSTFS_RENAME_BUFSIZE]; + int ret = -1; + struct HostFsRenameCmd cmd; + struct HostFsRenameResp resp; + int size; + + if((oldname == NULL) || (newname == NULL)) + { + MODPRINTF("Invalid file names for rename command\n"); + return -1; + } + + size = strlen(oldname) + strlen(newname) + 2; + if(size > sizeof(buf)) + { + MODPRINTF("Not enough buffer space for rename command, size %d, bufsize %d\n", size, sizeof(buf)); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_RENAME; + cmd.cmd.extralen = size; + cmd.fsnum = arg->fs_num; + + /* Fill buffer */ + strcpy(buf, oldname); + strcpy(buf+strlen(oldname)+1, newname); + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), buf, size, NULL, 0)) + { + /* Set the resultant fid into the arg structure */ + ret = resp.res; + DEBUG_PRINTF("Returned res %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending rename command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_chdir(PspIoDrvFileArg *arg, const char *dir) +{ + int ret = -1; + struct HostFsChdirCmd cmd; + struct HostFsChdirResp resp; + + if(dir == NULL) + { + MODPRINTF("Invalid dir name (NULL)\n"); + return -1; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_CHDIR; + cmd.cmd.extralen = strlen(dir)+1; + cmd.fsnum = arg->fs_num; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), dir, strlen(dir)+1, NULL, 0)) + { + ret = resp.res; + + DEBUG_PRINTF("Returned %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending chdir command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_mount(PspIoDrvFileArg *arg) +{ + /* Do nothing atm */ + return -1; +} + +static int io_umount(PspIoDrvFileArg *arg) +{ + /* Do nothing atm */ + return -1; +} + +static int io_devctl(PspIoDrvFileArg *arg, const char *name, unsigned int cmdno, void *indata, int inlen, void *outdata, int outlen) +{ + int ret = -1; + struct HostFsDevctlCmd cmd; + struct HostFsDevctlResp resp; + + if(name == NULL) + { + MODPRINTF("Invalid name (NULL)\n"); + return -1; + } + + /* Handle the get info devctl */ + if(cmdno == DEVCTL_GET_INFO) + { + void **p = (void **) indata; + if((p) && (*p)) + { + outdata = *p; + outlen = sizeof(struct DevctlGetInfo); + indata = NULL; + inlen = 0; + } + else + { + return -1; + } + } + + /* Ensure our lengths are zeroed */ + if(indata == NULL) + { + inlen = 0; + } + + if(outdata == NULL) + { + outlen = 0; + } + + memset(&cmd, 0, sizeof(cmd)); + memset(&resp, 0, sizeof(resp)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_DEVCTL; + cmd.cmd.extralen = inlen; + cmd.cmdno = cmdno; + cmd.fsnum = arg->fs_num; + cmd.outlen = outlen; + + if(usb_connected()) + { + if(command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), indata, inlen, outdata, outlen)) + { + ret = resp.res; + DEBUG_PRINTF("Returned res %d\n", resp.res); + } + else + { + MODPRINTF("Error in sending devctl command\n"); + } + } + else + { + MODPRINTF("%s: Error PC side not connected\n", __FUNCTION__); + } + + return ret; +} + +static int io_unknown(PspIoDrvFileArg *arg) +{ + /* Do nothing atm */ + MODPRINTF("Unknown command called\n"); + return -1; +} + +static PspIoDrvFuncs host_funcs = +{ + io_init, + io_exit, + io_open, + io_close, + io_read, + io_write, + io_lseek, + io_ioctl, + io_remove, + io_mkdir, + io_rmdir, + io_dopen, + io_dclose, + io_dread, + io_getstat, + io_chstat, + io_rename, + io_chdir, + io_mount, + io_umount, + io_devctl, + io_unknown, +}; + +static PspIoDrv host_driver = +{ + "host", 0x10, 0x800, "HOST", &host_funcs +}; + +int hostfs_init(void) +{ + int ret; + + (void) sceIoDelDrv("host"); /* Ignore error */ + ret = sceIoAddDrv(&host_driver); + if(ret < 0) + { + return ret; + } + + return 0; +} + +void hostfs_term(void) +{ + (void) sceIoDelDrv("host"); +} diff --git a/usbhostfs/kmode.S b/usbhostfs/kmode.S new file mode 100644 index 0000000..1da79d0 --- /dev/null +++ b/usbhostfs/kmode.S @@ -0,0 +1,13 @@ + + .set noreorder + .set noat + + .global psplinkSetK1 + .ent psplinkSetK1 + +psplinkSetK1: + move $v0, $k1 + jr $ra + move $k1, $a0 + + .end psplinkSetK1 diff --git a/usbhostfs/main.c b/usbhostfs/main.c new file mode 100644 index 0000000..3a74d0a --- /dev/null +++ b/usbhostfs/main.c @@ -0,0 +1,1231 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK USB HostFS main code + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/usbhostfs/main.c $ + * $Id: main.c 2189 2007-02-24 12:16:44Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbhostfs.h" +#include "usbasync.h" + +int psplinkSetK1(int k1); + +PSP_MODULE_INFO(MODULE_NAME, PSP_MODULE_KERNEL, 1, 1); + +/* Main USB event flags */ +enum UsbEvents +{ + USB_EVENT_ATTACH = 1, + USB_EVENT_DETACH = 2, + USB_EVENT_ASYNC = 4, + USB_EVENT_CONNECT = 8, + USB_EVENT_ALL = 0xFFFFFFFF +}; + +/* USB transfer event flags */ +enum UsbTransEvents +{ + USB_TRANSEVENT_BULKOUT_DONE = 1, + USB_TRANSEVENT_BULKIN_DONE = 2, +}; + +/* Main USB thread id */ +static SceUID g_thid = -1; +/* Main USB event flag */ +static SceUID g_mainevent = -1; +/* Main USB transfer event flag */ +static SceUID g_transevent = -1; +/* Asynchronous input event flag */ +static SceUID g_asyncevent = -1; +/* Main USB semaphore */ +static SceUID g_mainsema = -1; +/* Static bulkin request structure */ +static struct UsbdDeviceReq g_bulkin_req; +/* Static bulkout request structure */ +static struct UsbdDeviceReq g_bulkout_req; +/* Async request */ +static struct UsbdDeviceReq g_async_req; +/* Indicates we have a connection to the PC */ +static int g_connected = 0; +/* Buffers for async data */ +static struct AsyncEndpoint *g_async_chan[MAX_ASYNC_CHANNELS]; + +/* HI-Speed device descriptor */ +struct DeviceDescriptor devdesc_hi = +{ + .bLength = 18, + .bDescriptorType = 0x01, + .bcdUSB = 0x200, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize = 64, + .idVendor = 0, + .idProduct = 0, + .bcdDevice = 0x100, + .iManufacturer = 0, + .iProduct = 0, + .iSerialNumber = 0, + .bNumConfigurations = 1 +}; + +/* Hi-Speed configuration descriptor */ +struct ConfigDescriptor confdesc_hi = +{ + .bLength = 9, + .bDescriptorType = 2, + .wTotalLength = (9+9+(3*7)), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0xC0, + .bMaxPower = 0 +}; + +/* Hi-Speed interface descriptor */ +struct InterfaceDescriptor interdesc_hi = +{ + .bLength = 9, + .bDescriptorType = 4, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 3, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0x1, + .bInterfaceProtocol = 0xFF, + .iInterface = 1 +}; + +/* Hi-Speed endpoint descriptors */ +struct EndpointDescriptor endpdesc_hi[3] = +{ + { + .bLength = 7, + .bDescriptorType = 5, + .bEndpointAddress = 0x81, + .bmAttributes = 2, + .wMaxPacketSize = 512, + .bInterval = 0 + }, + { + .bLength = 7, + .bDescriptorType = 5, + .bEndpointAddress = 2, + .bmAttributes = 2, + .wMaxPacketSize = 512, + .bInterval = 0 + }, + { + .bLength = 7, + .bDescriptorType = 5, + .bEndpointAddress = 3, + .bmAttributes = 2, + .wMaxPacketSize = 512, + .bInterval = 0 + }, +}; + +/* Full-Speed device descriptor */ +struct DeviceDescriptor devdesc_full = +{ + .bLength = 18, + .bDescriptorType = 0x01, + .bcdUSB = 0x200, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize = 64, + .idVendor = 0, + .idProduct = 0, + .bcdDevice = 0x100, + .iManufacturer = 0, + .iProduct = 0, + .iSerialNumber = 0, + .bNumConfigurations = 1 +}; + +/* Full-Speed configuration descriptor */ +struct ConfigDescriptor confdesc_full = +{ + .bLength = 9, + .bDescriptorType = 2, + .wTotalLength = (9+9+(3*7)), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0xC0, + .bMaxPower = 0 +}; + +/* Full-Speed interface descriptor */ +struct InterfaceDescriptor interdesc_full = +{ + .bLength = 9, + .bDescriptorType = 4, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 3, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0x1, + .bInterfaceProtocol = 0xFF, + .iInterface = 1 +}; + +/* Full-Speed endpoint descriptors */ +struct EndpointDescriptor endpdesc_full[3] = +{ + { + .bLength = 7, + .bDescriptorType = 5, + .bEndpointAddress = 0x81, + .bmAttributes = 2, + .wMaxPacketSize = 64, + .bInterval = 0 + }, + { + .bLength = 7, + .bDescriptorType = 5, + .bEndpointAddress = 2, + .bmAttributes = 2, + .wMaxPacketSize = 64, + .bInterval = 0 + }, + { + .bLength = 7, + .bDescriptorType = 5, + .bEndpointAddress = 3, + .bmAttributes = 2, + .wMaxPacketSize = 64, + .bInterval = 0 + }, +}; + +/* String descriptor */ +unsigned char strp[] = +{ + 0x8, 0x3, '<', 0, '>', 0, 0, 0 +}; + +/* Endpoint blocks */ +struct UsbEndpoint endp[4] = { + { 0, 0, 0 }, + { 1, 0, 0 }, + { 2, 0, 0 }, + { 3, 0, 0 }, +}; + +/* Intefaces */ +struct UsbInterface intp = { + 0xFFFFFFFF, 0, 1, +}; + +/* Device request */ +int usb_request(int arg1, int arg2, struct DeviceRequest *req) +{ + DEBUG_PRINTF("func24 a1 %08X, a2 %08X, a3 %p\n", arg1, arg2, req); + DEBUG_PRINTF("ReqType %d, Request %d, Value %x, Index %x, Length %x\n", + req->bmRequestType, req->bRequest, req->wValue, req->wIndex, + req->wLength); + return 0; +} + +/* Unknown callback */ +int func28(int arg1, int arg2, int arg3) +{ + DEBUG_PRINTF("func28 a1 %08X, a2 %08X, a3 %08X\n", arg1, arg2, arg3); + return 0; +} + +/* Attach callback, speed 1=full, 2=hi */ +int usb_attach(int speed, void *arg2, void *arg3) +{ + DEBUG_PRINTF("usb_attach: speed %d, a2 %p, a3 %p\n", speed, arg2, arg3); + sceKernelSetEventFlag(g_mainevent, USB_EVENT_ATTACH); + + return 0; +} + +/* Detach callback */ +int usb_detach(int arg1, int arg2, int arg3) +{ + DEBUG_PRINTF("usb_detach: a1 %08X, a2 %08X, a3 %08X\n", arg1, arg2, arg3); + sceKernelSetEventFlag(g_mainevent, USB_EVENT_DETACH); + + return 0; +} + +/* Forward define the driver structure */ +extern struct UsbDriver g_driver; + +/* USB data structures for hi and full speed endpoints */ +struct UsbData usbdata[2]; + +/* Callback for when a bulkin request is done */ +int bulkin_req_done(struct UsbdDeviceReq *req, int arg2, int arg3) +{ + DEBUG_PRINTF("bulkin_req_done:\n"); + DEBUG_PRINTF("size %08X, unkc %08X, recvsize %08X\n", req->size, req->unkc, req->recvsize); + DEBUG_PRINTF("retcode %08X, unk1c %08X, arg %p\n", req->retcode, req->unk1c, req->arg); + sceKernelSetEventFlag(g_transevent, USB_TRANSEVENT_BULKIN_DONE); + return 0; +} + +/* Callback for when a bulkout request is done */ +int bulkout_req_done(struct UsbdDeviceReq *req, int arg2, int arg3) +{ + DEBUG_PRINTF("bulkout_req_done:\n"); + DEBUG_PRINTF("size %08X, unkc %08X, recvsize %08X\n", req->size, req->unkc, req->recvsize); + DEBUG_PRINTF("retcode %08X, unk1c %08X, arg %p\n", req->retcode, req->unk1c, req->arg); + sceKernelSetEventFlag(g_transevent, USB_TRANSEVENT_BULKOUT_DONE); + return 0; +} + +/* Callback for when a bulkout request is done */ +int async_req_done(struct UsbdDeviceReq *req, int arg2, int arg3) +{ + DEBUG_PRINTF("async_req_done:\n"); + DEBUG_PRINTF("size %08X, unkc %08X, recvsize %08X\n", req->size, req->unkc, req->recvsize); + DEBUG_PRINTF("retcode %08X, unk1c %08X, arg %p\n", req->retcode, req->unk1c, req->arg); + sceKernelSetEventFlag(g_mainevent, USB_EVENT_ASYNC); + return 0; +} + +/* Setup a bulkin request */ +int set_bulkin_req(void *data, int size) +{ + sceKernelDcacheWritebackRange(data, size); + memset(&g_bulkin_req, 0, sizeof(g_bulkin_req)); + g_bulkin_req.endp = &endp[1]; + g_bulkin_req.data = data; + g_bulkin_req.size = size; + g_bulkin_req.func = bulkin_req_done; + sceKernelClearEventFlag(g_transevent, ~USB_TRANSEVENT_BULKIN_DONE); + return sceUsbbdReqSend(&g_bulkin_req); +} + +/* Setup a bulkout request */ +int set_bulkout_req(void *data, int size) +{ + u32 addr; + u32 blockaddr; + u32 topaddr; + + /* Ensure address is uncached */ + addr = (u32) data; + blockaddr = (addr & ~63); + topaddr = (addr + size + 63) & ~63; + + if(blockaddr != addr) + { + MODPRINTF("Error read data not cache aligned\n"); + return -1; + } + + /* Invalidate range */ + sceKernelDcacheInvalidateRange((void*) blockaddr, topaddr - blockaddr); + memset(&g_bulkout_req, 0, sizeof(g_bulkout_req)); + g_bulkout_req.endp = &endp[2]; + g_bulkout_req.data = (void *) addr; + g_bulkout_req.size = size; + g_bulkout_req.func = bulkout_req_done; + sceKernelClearEventFlag(g_transevent, ~USB_TRANSEVENT_BULKOUT_DONE); + return sceUsbbdReqRecv(&g_bulkout_req); +} + +/* Read/Write buffer */ +unsigned char tx_buf[64*1024] __attribute__((aligned(64))); + +/* Read a block of data from the USB bus */ +int read_data(void *data, int size) +{ + int nextsize = 0; + int readlen = 0; + int ret; + u32 result; + + while(readlen < size) + { + nextsize = (size - readlen) > sizeof(tx_buf) ? sizeof(tx_buf) : (size - readlen); + if(set_bulkout_req(tx_buf, nextsize) < 0) + { + return -1; + } + + ret = sceKernelWaitEventFlag(g_transevent, USB_TRANSEVENT_BULKOUT_DONE, PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &result, NULL); + if(ret == 0) + { + if((g_bulkout_req.retcode == 0) && (g_bulkout_req.recvsize > 0)) + { + readlen += g_bulkout_req.recvsize; + memcpy(data, tx_buf, g_bulkout_req.recvsize); + data += g_bulkout_req.recvsize; + } + else + { + DEBUG_PRINTF("Error in BULKOUT request %d, %d\n", g_bulkout_req.retcode, g_bulkout_req.recvsize); + return -1; + } + } + else + { + MODPRINTF("Error waiting for BULKOUT %08X\n", ret); + return -1; + } + } + + return readlen; +} + +int write_data(const void *data, int size) +{ + int nextsize = 0; + int writelen = 0; + int ret; + u32 result; + + if((u32) data & 63) + { + while(writelen < size) + { + nextsize = (size - writelen) > sizeof(tx_buf) ? sizeof(tx_buf) : (size - writelen); + memcpy(tx_buf, data, nextsize); + set_bulkin_req(tx_buf, nextsize); + /* TODO: Add a timeout to the event flag wait */ + ret = sceKernelWaitEventFlag(g_transevent, USB_TRANSEVENT_BULKIN_DONE, PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &result, NULL); + if(ret == 0) + { + if((g_bulkin_req.retcode == 0) && (g_bulkin_req.recvsize > 0)) + { + writelen += g_bulkin_req.recvsize; + data += g_bulkin_req.recvsize; + } + else + { + MODPRINTF("Error in BULKIN request %d\n", g_bulkin_req.retcode); + return -1; + } + } + else + { + MODPRINTF("Error waiting for BULKIN %08X\n", ret); + return -1; + } + } + } + else + { + while(writelen < size) + { + nextsize = (size - writelen) > sizeof(tx_buf) ? sizeof(tx_buf) : (size - writelen); + set_bulkin_req((char *) data, nextsize); + /* TODO: Add a timeout to the event flag wait */ + ret = sceKernelWaitEventFlag(g_transevent, USB_TRANSEVENT_BULKIN_DONE, PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &result, NULL); + if(ret == 0) + { + if((g_bulkin_req.retcode == 0) && (g_bulkin_req.recvsize > 0)) + { + writelen += g_bulkin_req.recvsize; + data += g_bulkin_req.recvsize; + } + else + { + MODPRINTF("Error in BULKIN request %d\n", g_bulkin_req.retcode); + return -1; + } + } + else + { + MODPRINTF("Error waiting for BULKIN %08X\n", ret); + return -1; + } + } + } + + return writelen; +} + +int write_data_large(const void *data, int size) +{ + int ret; + u32 result; + + if((u32) data & 63) + { + return -1; + } + + set_bulkin_req((char *) data, size); + ret = sceKernelWaitEventFlag(g_transevent, USB_TRANSEVENT_BULKIN_DONE, PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &result, NULL); + if(ret == 0) + { + if((g_bulkin_req.retcode == 0) && (g_bulkin_req.recvsize > 0)) + { + } + else + { + MODPRINTF("Error in BULKIN request %d\n", g_bulkin_req.retcode); + return -1; + } + } + else + { + MODPRINTF("Error waiting for BULKIN %08X\n", ret); + return -1; + } + + return size; +} + +/* Exchange a HOSTFS command with the PC host */ +int command_xchg(void *outcmd, int outcmdlen, void *incmd, int incmdlen, const void *outdata, + int outlen, void *indata, int inlen) +{ + struct HostFsCmd *cmd; + struct HostFsCmd *resp; + int ret = 0; + int err = 0; + + /* TODO: Set timeout on semaphore */ + err = sceKernelWaitSema(g_mainsema, 1, NULL); + if(err < 0) + { + MODPRINTF("Error waiting on xchg semaphore %08X\n", err); + return 0; + } + + do + { + cmd = (struct HostFsCmd *) outcmd; + resp = (struct HostFsCmd *) incmd; + + if(outcmdlen > 0) + { + err = write_data(outcmd, outcmdlen); + if(err != outcmdlen) + { + MODPRINTF("Error writing command %08X %d\n", cmd->command, err); + break; + } + } + + if(outlen > 0) + { + err = write_data(outdata, outlen); + if(err != outlen) + { + MODPRINTF("Error writing command data %08X, %d\n", cmd->command, err); + break; + } + } + + if(incmdlen > 0) + { + err = read_data(incmd, incmdlen); + if(err != incmdlen) + { + MODPRINTF("Error reading response for %08X %d\n", cmd->command, err); + break; + } + + if((resp->magic != HOSTFS_MAGIC) && (resp->command != cmd->command)) + { + MODPRINTF("Invalid response packet magic: %08X, command: %08X\n", resp->magic, resp->command); + break; + } + + DEBUG_PRINTF("resp->magic %08X, resp->command %08X, resp->extralen %d\n", + resp->magic, resp->command, resp->extralen); + + /* TODO: Should add checks for inlen being less that extra len */ + if((resp->extralen > 0) && (inlen > 0)) + { + err = read_data(indata, resp->extralen); + if(err != resp->extralen) + { + MODPRINTF("Error reading input data %08X, %d\n", cmd->command, err); + break; + } + } + } + + ret = 1; + } + while(0); + + (void) sceKernelSignalSema(g_mainsema, 1); + + return ret; +} + +int usbLockBus(void) +{ + int k1; + int err; + + k1 = psplinkSetK1(0); + err = sceKernelWaitSema(g_mainsema, 1, NULL); + psplinkSetK1(k1); + + return err; +} + +int usbUnlockBus(void) +{ + int k1; + int err; + + k1 = psplinkSetK1(0); + err = sceKernelSignalSema(g_mainsema, 1); + psplinkSetK1(k1); + + return err; +} + +/* Send an async write */ +int send_async(void *data, int len) +{ + int ret = 0; + int err = 0; + + /* TODO: Set timeout on semaphore */ + err = sceKernelWaitSema(g_mainsema, 1, NULL); + if(err < 0) + { + MODPRINTF("Error waiting on xchg semaphore %08X\n", err); + return 0; + } + + do + { + if((data) && (len > 0)) + { + err = write_data(data, len); + if(err != len) + { + MODPRINTF("Error writing async command %d\n", err); + break; + } + } + + ret = 1; + } + while(0); + + (void) sceKernelSignalSema(g_mainsema, 1); + + return ret; +} + +/* Send the hello command, indicates we are here */ +int send_hello_cmd(void) +{ + struct HostFsHelloCmd cmd; + struct HostFsHelloResp resp; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd.magic = HOSTFS_MAGIC; + cmd.cmd.command = HOSTFS_CMD_HELLO; + + return command_xchg(&cmd, sizeof(cmd), &resp, sizeof(resp), NULL, 0, NULL, 0); +} + +/* Setup a async request */ +int set_ayncreq(void *data, int size) +{ + u32 addr; + u32 blockaddr; + u32 topaddr; + + /* Ensure address is uncached */ + addr = (u32) data; + blockaddr = (addr & ~63); + topaddr = (addr + size + 63) & ~63; + + if(blockaddr != addr) + { + MODPRINTF("Error read data not cache aligned\n"); + return -1; + } + + /* Invalidate range */ + sceKernelDcacheInvalidateRange((void*) blockaddr, topaddr - blockaddr); + memset(&g_async_req, 0, sizeof(g_async_req)); + g_async_req.endp = &endp[3]; + g_async_req.data = (void *) addr; + g_async_req.size = size; + g_async_req.func = async_req_done; + sceKernelClearEventFlag(g_mainevent, ~USB_EVENT_ASYNC); + return sceUsbbdReqRecv(&g_async_req); +} + +/* Call to ensure we are connected to the USB host */ +int usb_connected(void) +{ + return g_connected; +} + +char async_data[512] __attribute__((aligned(64))); + +void fill_async(void *async_data, int len) +{ + struct AsyncCommand *cmd; + unsigned char *data; + int sizeleft; + int intc; + + if(len > sizeof(struct AsyncCommand)) + { + len -= sizeof(struct AsyncCommand); + data = async_data + sizeof(struct AsyncCommand); + cmd = (struct AsyncCommand *) async_data; + + DEBUG_PRINTF("magic %08X, channel %d\n", cmd->magic, cmd->channel); + intc = pspSdkDisableInterrupts(); + if((cmd->magic == ASYNC_MAGIC) && (cmd->channel >= 0) && (cmd->channel < MAX_ASYNC_CHANNELS) && (g_async_chan[cmd->channel])) + { + struct AsyncEndpoint *pEndp = g_async_chan[cmd->channel]; + sizeleft = len < (MAX_ASYNC_BUFFER - pEndp->size) ? len + : (MAX_ASYNC_BUFFER - pEndp->size); + while(sizeleft > 0) + { + pEndp->buffer[pEndp->write_pos++] = *data++; + pEndp->write_pos %= MAX_ASYNC_BUFFER; + pEndp->size++; + sizeleft--; + } + sceKernelSetEventFlag(g_asyncevent, (1 << cmd->channel)); + DEBUG_PRINTF("Async chan %d - read_pos %d - write_pos %d - size %d\n", cmd->channel, + pEndp->read_pos, pEndp->write_pos, + pEndp->size); + } + else + { + MODPRINTF("Error in command header\n"); + } + pspSdkEnableInterrupts(intc); + } +} + +int usbAsyncRegister(unsigned int chan, struct AsyncEndpoint *endp) +{ + int intc; + int ret = -1; + + intc = pspSdkDisableInterrupts(); + do + { + if(endp == NULL) + { + break; + } + + if(chan == ASYNC_ALLOC_CHAN) + { + int i; + + for(i = ASYNC_USER; i < MAX_ASYNC_CHANNELS; i++) + { + if(g_async_chan[i] == NULL) + { + chan = i; + break; + } + } + + if(i == MAX_ASYNC_CHANNELS) + { + break; + } + } + else + { + if((chan >= MAX_ASYNC_CHANNELS) || (g_async_chan[chan] != NULL)) + { + break; + } + } + + g_async_chan[chan] = endp; + usbAsyncFlush(chan); + + ret = chan; + } + while(0); + pspSdkEnableInterrupts(intc); + + return ret; +} + +int usbAsyncUnregister(unsigned int chan) +{ + int intc; + int ret = -1; + + intc = pspSdkDisableInterrupts(); + do + { + if((chan >= MAX_ASYNC_CHANNELS) || (g_async_chan[chan] == NULL)) + { + break; + } + + g_async_chan[chan] = NULL; + + ret = 0; + } + while(0); + pspSdkEnableInterrupts(intc); + + return ret; +} + +int usbAsyncReadWithTimeout(unsigned int chan, unsigned char *data, int len, int timeout) +{ + int ret; + int intc; + int i; + int k1; + SceUInt *pTimeout = NULL; + + k1 = psplinkSetK1(0); + + if((chan >= MAX_ASYNC_CHANNELS) || (g_async_chan[chan] == NULL)) + { + return -1; + } + + if(timeout >= 0) + { + pTimeout = (SceUInt*) &timeout; + } + + ret = sceKernelWaitEventFlag(g_asyncevent, 1 << chan, PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, NULL, pTimeout); + if(ret < 0) + { + return -1; + } + + intc = pspSdkDisableInterrupts(); + len = len < g_async_chan[chan]->size ? len : g_async_chan[chan]->size; + for(i = 0; i < len; i++) + { + data[i] = g_async_chan[chan]->buffer[g_async_chan[chan]->read_pos++]; + g_async_chan[chan]->read_pos %= MAX_ASYNC_BUFFER; + g_async_chan[chan]->size--; + } + + if(g_async_chan[chan]->size != 0) + { + sceKernelSetEventFlag(g_asyncevent, 1 << chan); + } + pspSdkEnableInterrupts(intc); + + psplinkSetK1(k1); + + return len; +} + +int usbAsyncRead(unsigned int chan, unsigned char *data, int len) +{ + return usbAsyncReadWithTimeout(chan, data, len, -1); +} + +void usbAsyncFlush(unsigned int chan) +{ + int intc; + + if((chan >= MAX_ASYNC_CHANNELS) || (g_async_chan[chan] == NULL)) + { + return; + } + + intc = pspSdkDisableInterrupts(); + g_async_chan[chan]->size = 0; + g_async_chan[chan]->read_pos = 0; + g_async_chan[chan]->write_pos = 0; + sceKernelClearEventFlag(g_asyncevent, ~(1 << chan)); + pspSdkEnableInterrupts(intc); +} + +int usbAsyncWrite(unsigned int chan, const void *data, int len) +{ + int ret = -1; + char buffer[512]; + struct AsyncCommand *cmd; + int written = 0; + int k1; + + k1 = psplinkSetK1(0); + + do + { + if(!usb_connected()) + { + DEBUG_PRINTF("Error PC side not connected\n"); + break; + } + + cmd = (struct AsyncCommand *) buffer; + cmd->magic = ASYNC_MAGIC; + cmd->channel = chan; + + while(written < len) + { + int size; + + size = (len-written) > (sizeof(buffer)-sizeof(struct AsyncCommand)) ? (sizeof(buffer)-sizeof(struct AsyncCommand)) : (len-written); + memcpy(&buffer[sizeof(struct AsyncCommand)], data+written, size); + if(send_async(buffer, size + sizeof(struct AsyncCommand))) + { + written += size; + } + else + { + break; + } + } + + ret = written; + } + while(0); + + psplinkSetK1(k1); + + return ret; +} + +int usbWriteBulkData(int chan, const void *data, int len) +{ + int ret = -1; + int err; + struct BulkCommand cmd; + int k1; + + k1 = psplinkSetK1(0); + + do + { + if(!usb_connected()) + { + DEBUG_PRINTF("Error PC side not connected\n"); + break; + } + + if((len <= 0) || (len > HOSTFS_BULK_MAXWRITE)) + { + MODPRINTF("Invalid length %d\n", len); + break; + } + + cmd.magic = BULK_MAGIC; + cmd.channel = chan; + cmd.size = len; + + /* TODO: Set timeout on semaphore */ + err = sceKernelWaitSema(g_mainsema, 1, NULL); + if(err < 0) + { + MODPRINTF("Error waiting on xchg semaphore %08X\n", err); + break; + } + + do + { + err = write_data(&cmd, sizeof(cmd)); + if(err != sizeof(cmd)) + { + MODPRINTF("Error writing bulk header %d\n", err); + err = -1; + break; + } + + //err = write_data(data, len); + err = write_data_large(data, len); + if(err != len) + { + MODPRINTF("Error writing bulk data %d\n", err); + err = -1; + break; + } + } + while(0); + + (void) sceKernelSignalSema(g_mainsema, 1); + if(err >= 0) + { + ret = len; + } + } + while(0); + + psplinkSetK1(k1); + + return ret; +} + +int usbWaitForConnect(void) +{ + int ret; + + while(g_mainevent < 0) + { + sceKernelDelayThread(100000); + } + + ret = sceKernelWaitEventFlag(g_mainevent, USB_EVENT_CONNECT, PSP_EVENT_WAITOR, NULL, NULL); + if(ret == 0) + { + return 1; + } + + return 0; +} + +/* USB thread to handle attach/detach */ +int usb_thread(SceSize size, void *argp) +{ + int ret; + u32 result; + + DEBUG_PRINTF("USB Thread Started\n"); + while(1) + { + ret = sceKernelWaitEventFlag(g_mainevent, USB_EVENT_ATTACH | USB_EVENT_DETACH | USB_EVENT_ASYNC + , PSP_EVENT_WAITOR | PSP_EVENT_WAITCLEAR, &result, NULL); + if(ret < 0) + { + DEBUG_PRINTF("Error waiting on event flag %08X\n", ret); + sceKernelExitDeleteThread(0); + } + + if(result & USB_EVENT_ASYNC) + { + DEBUG_PRINTF("Async Request Done %d %d\n", g_async_req.retcode, g_async_req.recvsize); + if((g_async_req.retcode == 0) && (g_async_req.recvsize > 0)) + { + fill_async(async_data, g_async_req.recvsize); + set_ayncreq(async_data, sizeof(async_data)); + } + } + + if(result & USB_EVENT_DETACH) + { + g_connected = 0; + sceKernelClearEventFlag(g_mainevent, ~USB_EVENT_CONNECT); + DEBUG_PRINTF("USB Detach occurred\n"); + } + + if(result & USB_EVENT_ATTACH) + { + uint32_t magic; + g_connected = 0; + sceKernelClearEventFlag(g_mainevent, ~USB_EVENT_CONNECT); + DEBUG_PRINTF("USB Attach occurred\n"); + if(read_data(&magic, sizeof(magic)) == sizeof(magic)) + { + if(magic == HOSTFS_MAGIC) + { + if(send_hello_cmd()) + { + set_ayncreq(async_data, sizeof(async_data)); + g_connected = 1; + sceKernelSetEventFlag(g_mainevent, USB_EVENT_CONNECT); + } + } + } + } + } + + return 0; +} + +/* USB start function */ +int start_func(int size, void *p) +{ + int ret; + + DEBUG_PRINTF("Start Function %p\n", p); + + /* Fill in the descriptor tables */ + memset(usbdata, 0, sizeof(usbdata)); + + memcpy(usbdata[0].devdesc, &devdesc_hi, sizeof(devdesc_hi)); + usbdata[0].config.pconfdesc = &usbdata[0].confdesc; + usbdata[0].config.pinterfaces = &usbdata[0].interfaces; + usbdata[0].config.pinterdesc = &usbdata[0].interdesc; + usbdata[0].config.pendp = usbdata[0].endp; + memcpy(usbdata[0].confdesc.desc, &confdesc_hi, sizeof(confdesc_hi)); + usbdata[0].confdesc.pinterfaces = &usbdata[0].interfaces; + usbdata[0].interfaces.pinterdesc[0] = &usbdata[0].interdesc; + usbdata[0].interfaces.intcount = 1; + memcpy(usbdata[0].interdesc.desc, &interdesc_hi, sizeof(interdesc_hi)); + usbdata[0].interdesc.pendp = usbdata[0].endp; + memcpy(usbdata[0].endp[0].desc, &endpdesc_hi[0], sizeof(endpdesc_hi[0])); + memcpy(usbdata[0].endp[1].desc, &endpdesc_hi[1], sizeof(endpdesc_hi[1])); + memcpy(usbdata[0].endp[2].desc, &endpdesc_hi[2], sizeof(endpdesc_hi[2])); + + memcpy(usbdata[1].devdesc, &devdesc_full, sizeof(devdesc_full)); + usbdata[1].config.pconfdesc = &usbdata[1].confdesc; + usbdata[1].config.pinterfaces = &usbdata[1].interfaces; + usbdata[1].config.pinterdesc = &usbdata[1].interdesc; + usbdata[1].config.pendp = usbdata[1].endp; + memcpy(usbdata[1].confdesc.desc, &confdesc_full, sizeof(confdesc_full)); + usbdata[1].confdesc.pinterfaces = &usbdata[1].interfaces; + usbdata[1].interfaces.pinterdesc[0] = &usbdata[1].interdesc; + usbdata[1].interfaces.intcount = 1; + memcpy(usbdata[1].interdesc.desc, &interdesc_full, sizeof(interdesc_full)); + usbdata[1].interdesc.pendp = usbdata[1].endp; + memcpy(usbdata[1].endp[0].desc, &endpdesc_full[0], sizeof(endpdesc_full[0])); + memcpy(usbdata[1].endp[1].desc, &endpdesc_full[1], sizeof(endpdesc_full[1])); + memcpy(usbdata[1].endp[2].desc, &endpdesc_full[2], sizeof(endpdesc_full[2])); + + g_driver.devp_hi = usbdata[0].devdesc; + g_driver.confp_hi = &usbdata[0].config; + g_driver.devp = usbdata[1].devdesc; + g_driver.confp = &usbdata[1].config; + + DEBUG_PRINTF("pusbdata %p\n", &usbdata); + + memset(g_async_chan, 0, sizeof(g_async_chan)); + + g_mainevent = sceKernelCreateEventFlag("USBEvent", 0x200, 0, NULL); + if(g_mainevent < 0) + { + MODPRINTF("Couldn't create event flag %08X\n", g_mainevent); + return -1; + } + + g_transevent = sceKernelCreateEventFlag("USBEventTrans", 0x200, 0, NULL); + if(g_transevent < 0) + { + MODPRINTF("Couldn't create trans event flag %08X\n", g_transevent); + return -1; + } + + g_asyncevent = sceKernelCreateEventFlag("USBEventAsync", 0x200, 0, NULL); + if(g_asyncevent < 0) + { + MODPRINTF("Couldn't create async event flag %08X\n", g_asyncevent); + return -1; + } + + g_mainsema = sceKernelCreateSema("USBSemaphore", 0, 1, 1, NULL); + if(g_mainsema < 0) + { + MODPRINTF("Couldn't create semaphore %08X\n", g_mainsema); + return -1; + } + + g_thid = sceKernelCreateThread("USBThread", usb_thread, 10, 0x10000, 0, NULL); + if(g_thid < 0) + { + MODPRINTF("Couldn't create usb thread %08X\n", g_thid); + return -1; + } + + ret = hostfs_init(); + DEBUG_PRINTF("hostfs_init: %d\n", ret); + + if(sceKernelStartThread(g_thid, 0, NULL)) + { + MODPRINTF("Couldn't start usb thread\n"); + return -1; + } + + return 0; +} + +/* USB stop function */ +int stop_func(int size, void *p) +{ + DEBUG_PRINTF("Stop function %p\n", p); + + if(g_thid >= 0) + { + /* Should really just signal for completion */ + sceKernelTerminateDeleteThread(g_thid); + g_thid = -1; + } + + if(g_mainevent >= 0) + { + sceKernelDeleteEventFlag(g_mainevent); + g_mainevent = -1; + } + + if(g_transevent >= 0) + { + sceKernelDeleteEventFlag(g_transevent); + g_mainevent = -1; + } + + if(g_asyncevent >= 0) + { + sceKernelDeleteEventFlag(g_asyncevent); + g_asyncevent = -1; + } + + if(g_mainsema >= 0) + { + sceKernelDeleteSema(g_mainsema); + g_mainsema = -1; + } + + return 0; +} + +/* USB host driver */ +struct UsbDriver g_driver = +{ + HOSTFSDRIVER_NAME, + 4, + endp, + &intp, + NULL, NULL, NULL, NULL, + (struct StringDescriptor *) strp, + usb_request, func28, usb_attach, usb_detach, + 0, + start_func, + stop_func, + NULL +}; + +/* Entry point */ +int module_start(SceSize args, void *argp) +{ + int ret; + + ret = sceUsbbdRegister(&g_driver); + memset(g_async_chan, 0, sizeof(g_async_chan)); + DEBUG_PRINTF("sceUsbbdRegister %08X\n", ret); + DEBUG_PRINTF("g_driver 0x%p\n", &g_driver); + MODPRINTF("USB HostFS Driver (c) TyRaNiD 2k6\n"); + + return 0; +} + +/* Module stop entry */ +int module_stop(SceSize args, void *argp) +{ + int ret; + + ret = sceUsbbdUnregister(&g_driver); + DEBUG_PRINTF("sceUsbbdUnregister %08X\n", ret); + hostfs_term(); + + return 0; +} diff --git a/usbhostfs/usbasync.h b/usbhostfs/usbasync.h new file mode 100644 index 0000000..c7bfa7d --- /dev/null +++ b/usbhostfs/usbasync.h @@ -0,0 +1,126 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * usbasync.h - PSPLINK USB Asynchronous Data Provider + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/usbhostfs/usbasync.h $ + * $Id: usbasync.h 2177 2007-02-15 17:28:02Z tyranid $ + */ +#ifndef __USBASYNC_H__ +#define __USBASYNC_H__ + +#include + +#define MAX_ASYNC_BUFFER 4096 + +#define ASYNC_USER 4 + +struct AsyncEndpoint +{ + unsigned char buffer[MAX_ASYNC_BUFFER]; + int read_pos; + int write_pos; + int size; +}; + +#define MAX_ASYNC_CHANNELS 8 +#define ASYNC_ALLOC_CHAN ((unsigned int) (-1)) + +/** + * Wait for the USB connection to be established + * + * @return 0 on connected, < 0 on error + */ +int usbWaitForConnect(void); + +/** + * Register an asyncronous provider + * @param chan - The channel number to register (0->3 are reserved for psplink use) + * If you dont care about what channel number to provide then pass ASYNC_ALLOC_CHAN + * @param endp - Pointer to an AsyncEndpoint structure + * + * @return channel number on success, < 0 on error + */ +int usbAsyncRegister(unsigned int chan, struct AsyncEndpoint *endp); + +/** + * Unregister an asyncronous provider + * + * @param chan - The channel to unregister + * + * @return 0 on success, < 0 on error + */ +int usbAsyncUnregister(unsigned int chan); + +/** + * Write data to the specified async channel + * + * @param chan - The channel to write to + * @param data - The data set to write + * @param len - The length of the data + * + * @return The number of bytes written, < 0 on error + */ +int usbAsyncWrite(unsigned int chan, const void *data, int len); + +/** + * Read data from the specified async channel + * + * @param chan - The channel to read from + * @param data - The data block to read into + * @param len - The length of the data to read + * + * @return The number of bytes read, < 0 on error + */ +int usbAsyncRead(unsigned int chan, unsigned char *data, int len); + +/** + * Read data from the specified async channel with a timeout + * + * @param chan - The channel to read from + * @param data - The data block to read into + * @param len - The length of the data to read + * @param timeout - The timeout, if < 0 then dont timeout, otherwise number + * of microseconds + * + * @return The number of bytes read, < 0 on error + */ +int usbAsyncReadWithTimeout(unsigned int chan, unsigned char *data, int len, int timeout); + +/** + * Flush the read buffer of an async channel + * + * @param chan - The channel to flush + */ +void usbAsyncFlush(unsigned int chan); + +/** + * Write a large transfer to an async channel + * + * @param chan - The channel to write to + * @param data - Pointer to the data to write (should be 16byte aligned) + * @param size - Size of data set to write + * + * @return the number of bytes written, < 0 on error + */ +int usbWriteBulkData(int chan, const void *data, int len); + +/** + * Lock the USB bus (normally to ensure no threads are using it) + * + * @return < 0 on error + */ +int usbLockBus(void); + +/** + * Unlock the USB bus + * + * @return < 0 on error + */ +int usbUnlockBus(void); + +#endif diff --git a/usbhostfs/usbhostfs.h b/usbhostfs/usbhostfs.h new file mode 100644 index 0000000..5521d91 --- /dev/null +++ b/usbhostfs/usbhostfs.h @@ -0,0 +1,373 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * usbhostfs.h - PSPLINK USB HostFS command header + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/usbhostfs/usbhostfs.h $ + * $Id: usbhostfs.h 2263 2007-06-28 20:00:22Z tyranid $ + */ +#ifndef __USBHOSTFS_H__ +#define __USBHOSTFS_H__ + +#include + +#define MODULE_NAME "USBHostFS" +#define HOSTFSDRIVER_NAME "USBHostFSDriver" +#define HOSTFSDRIVER_PID (0x1C9) +#define SONY_VID (0x54C) + +#define HOSTFS_MAGIC 0x782F0812 +#define ASYNC_MAGIC 0x782F0813 +#define BULK_MAGIC 0x782F0814 + +#define HOSTFS_PATHMAX (4096) + +#define HOSTFS_MAX_BLOCK (64*1024) + +#define HOSTFS_RENAME_BUFSIZE (1024) + +#define HOSTFS_BULK_MAXWRITE (1024*1024) + +#define DEVCTL_GET_INFO 0x02425818 + +struct DevctlGetInfo +{ + /* Total number of blocks */ + unsigned int btotal; + /* Total number of free blocks */ + unsigned int bfree; + /* Unknown */ + unsigned int unk; + /* Sector size */ + unsigned int ssize; + /* Number of sectors per block */ + unsigned int sects; +}; + +enum USB_ASYNC_CHANNELS +{ + ASYNC_SHELL = 0, + ASYNC_GDB = 1, + ASYNC_STDOUT = 2, + ASYNC_STDERR = 3, +}; + +#define MAX_ASYNC_CHANNELS 8 + +enum HostFsCommands +{ + HOSTFS_CMD_HELLO = 0x8FFC0000, + HOSTFS_CMD_BYE = 0x8FFC0001, + HOSTFS_CMD_OPEN = 0x8FFC0002, + HOSTFS_CMD_CLOSE = 0x8FFC0003, + HOSTFS_CMD_READ = 0x8FFC0004, + HOSTFS_CMD_WRITE = 0x8FFC0005, + HOSTFS_CMD_LSEEK = 0x8FFC0006, + HOSTFS_CMD_REMOVE = 0x8FFC0007, + HOSTFS_CMD_MKDIR = 0x8FFC0008, + HOSTFS_CMD_RMDIR = 0x8FFC0009, + HOSTFS_CMD_DOPEN = 0x8FFC000A, + HOSTFS_CMD_DREAD = 0x8FFC000B, + HOSTFS_CMD_DCLOSE = 0x8FFC000C, + HOSTFS_CMD_GETSTAT = 0x8FFC000D, + HOSTFS_CMD_CHSTAT = 0x8FFC000E, + HOSTFS_CMD_RENAME = 0x8FFC000F, + HOSTFS_CMD_CHDIR = 0x8FFC0010, + HOSTFS_CMD_IOCTL = 0x8FFC0011, + HOSTFS_CMD_DEVCTL = 0x8FFC0012 +}; + +struct HostFsTimeStamp +{ + uint16_t year; + uint16_t month; + uint16_t day; + uint16_t hour; + uint16_t minute; + uint16_t second; +} __attribute__((packed)); + +struct HostFsCmd +{ + uint32_t magic; + uint32_t command; + uint32_t extralen; +} __attribute__((packed)); + +struct HostFsHelloCmd +{ + struct HostFsCmd cmd; +} __attribute__((packed)); + +struct HostFsHelloResp +{ + struct HostFsCmd cmd; +} __attribute__((packed)); + +struct HostFsByeCmd +{ + struct HostFsCmd cmd; +} __attribute__((packed)); + +struct HostFsByeResp +{ + struct HostFsCmd cmd; +} __attribute__((packed)); + +struct HostFsOpenCmd +{ + struct HostFsCmd cmd; + uint32_t mode; + uint32_t mask; + uint32_t fsnum; +} __attribute__((packed)); + +struct HostFsOpenResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsCloseCmd +{ + struct HostFsCmd cmd; + int32_t fid; +} __attribute__((packed)); + +struct HostFsCloseResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsReadCmd +{ + struct HostFsCmd cmd; + int32_t fid; + int32_t len; +} __attribute__((packed)); + +struct HostFsReadResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsWriteCmd +{ + struct HostFsCmd cmd; + int32_t fid; +} __attribute__((packed)); + +struct HostFsWriteResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsLseekCmd +{ + struct HostFsCmd cmd; + int32_t fid; + int64_t ofs; + int32_t whence; +} __attribute__((packed)); + +struct HostFsLseekResp +{ + struct HostFsCmd cmd; + int32_t res; + int64_t ofs; +} __attribute__((packed)); + +struct HostFsIoctlCmd +{ + struct HostFsCmd cmd; + int32_t fid; + uint32_t cmdno; + int32_t outlen; +} __attribute__((packed)); + +struct HostFsIoctlResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsDevctlCmd +{ + struct HostFsCmd cmd; + uint32_t cmdno; + uint32_t fsnum; + int32_t outlen; +} __attribute__((packed)); + +struct HostFsDevctlResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsRemoveCmd +{ + struct HostFsCmd cmd; + uint32_t fsnum; +} __attribute__((packed)); + +struct HostFsRemoveResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsDopenCmd +{ + struct HostFsCmd cmd; + uint32_t fsnum; +} __attribute__((packed)); + +struct HostFsDopenResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsDreadCmd +{ + struct HostFsCmd cmd; + int32_t did; +} __attribute__((packed)); + +struct HostFsDreadResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsDcloseCmd +{ + struct HostFsCmd cmd; + int32_t did; +} __attribute__((packed)); + +struct HostFsDcloseResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsMkdirCmd +{ + struct HostFsCmd cmd; + unsigned int mode; + uint32_t fsnum; +} __attribute__((packed)); + +struct HostFsMkdirResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsRmdirCmd +{ + struct HostFsCmd cmd; + uint32_t fsnum; +} __attribute__((packed)); + +struct HostFsRmdirResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsGetstatCmd +{ + struct HostFsCmd cmd; + uint32_t fsnum; +} __attribute__((packed)); + +struct HostFsGetstatResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsChstatCmd +{ + struct HostFsCmd cmd; + int32_t bits; + uint32_t mode; + int64_t size; + /** Access time. */ + struct HostFsTimeStamp atime; + /** Modification time. */ + struct HostFsTimeStamp mtime; + uint32_t fsnum; +} __attribute__((packed)); + +struct HostFsChstatResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsRenameCmd +{ + struct HostFsCmd cmd; + uint32_t fsnum; +} __attribute__((packed)); + +struct HostFsRenameResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct HostFsChdirCmd +{ + struct HostFsCmd cmd; + uint32_t fsnum; +} __attribute__((packed)); + +struct HostFsChdirResp +{ + struct HostFsCmd cmd; + int32_t res; +} __attribute__((packed)); + +struct AsyncCommand +{ + uint32_t magic; + uint32_t channel; +} __attribute__((packed)); + +struct BulkCommand +{ + uint32_t magic; + uint32_t channel; + uint32_t size; +} __attribute__((packed)); + +#ifndef PC_SIDE + +#ifdef DEBUG +#define DEBUG_PRINTF(fmt, ...) Kprintf("%s: " fmt, MODULE_NAME, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +#define MODPRINTF DEBUG_PRINTF + +int usb_connected(void); +int command_xchg(void *outcmd, int outcmdlen, void *incmd, int incmdlen, const void *outdata, + int outlen, void *indata, int inlen); +int hostfs_init(void); +void hostfs_term(void); +#endif + +#endif diff --git a/usbhostfs_pc/50-psplink.rules b/usbhostfs_pc/50-psplink.rules new file mode 100644 index 0000000..4583ad5 --- /dev/null +++ b/usbhostfs_pc/50-psplink.rules @@ -0,0 +1 @@ +SUBSYSTEM=="usb_device", SYSFS{idVendor}=="054c", SYSFS{idProduct}=="01c9" MODE="0666" diff --git a/usbhostfs_pc/Makefile b/usbhostfs_pc/Makefile new file mode 100644 index 0000000..ccd3bc2 --- /dev/null +++ b/usbhostfs_pc/Makefile @@ -0,0 +1,39 @@ +OUTPUT=usbhostfs_pc +OBJS=main.o +LIBS=-lusb -lpthread +CFLAGS=-Wall -ggdb -I../usbhostfs -DPC_SIDE -D_FILE_OFFSET_BITS=64 -I. -O2 +LDFLAGS=-L. + +PREFIX=$(shell psp-config --pspdev-path 2> /dev/null) + +ifdef BUILD_BIGENDIAN +CFLAGS += -DBUILD_BIGENDIAN +endif + +ifdef NO_UID_CHECK +CFLAGS += -DNO_UID_CHECK +endif + +ifdef READLINE_SHELL +CFLAGS += -DREADLINE_SHELL +LIBS += -lreadline -lcurses +endif + +ifdef BUILD_WIN32 +CFLAGS += -I../windows/lib -Wno-format +LDFLAGS += -L../windows/lib +OUTPUT := $(OUTPUT).exe +endif + +all: $(OUTPUT) + +$(OUTPUT): $(OBJS) + $(LINK.c) $(LDFLAGS) -o $@ $^ $(LIBS) + +install: $(OUTPUT) + @echo "Installing $(OUTPUT)..." + @if ( test $(PREFIX) ); then { mkdir -p $(PREFIX)/bin && cp $(OUTPUT) $(PREFIX)/bin; } else { echo "Error: psp-config not found!"; exit 1; } fi + @echo "Done!" + +clean: + rm -f $(OUTPUT) *.o diff --git a/usbhostfs_pc/README b/usbhostfs_pc/README new file mode 100644 index 0000000..d4bea01 --- /dev/null +++ b/usbhostfs_pc/README @@ -0,0 +1 @@ +PC side of the PSP USB Host filing system, read the manual for further instructions. diff --git a/usbhostfs_pc/main.c b/usbhostfs_pc/main.c new file mode 100644 index 0000000..46f033e --- /dev/null +++ b/usbhostfs_pc/main.c @@ -0,0 +1,3173 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - Main code for PC side of USB HostFS + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.pspdev.org/psp/branches/psplinkusb/usbhostfs_pc/main.c $ + * $Id: main.c 2177 2007-02-15 17:28:02Z tyranid $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __CYGWIN__ +#include +#define NO_UID_CHECK +/* Define out set* and get* calls for cygwin as they are unnecessary and can cause issues */ +#define seteuid(x) +#define setegid(x) +#define getuid() +#define getgid() +#else +#include +#endif + +#ifdef READLINE_SHELL +#include +#include +#endif + +#include "psp_fileio.h" + +#define MAX_FILES 256 +#define MAX_DIRS 256 +#define MAX_TOKENS 256 + +#define BASE_PORT 10000 + +#ifdef __CYGWIN__ +#define USB_TIMEOUT 1000 +#else +#define USB_TIMEOUT 0 +#endif + +#define MAX_HOSTDRIVES 8 + +#ifndef SOL_TCP +#define SOL_TCP getprotobyname("TCP")->p_proto +#endif + +/* Contains the paths for a single hist drive */ +struct HostDrive +{ + char rootdir[PATH_MAX]; + char currdir[PATH_MAX]; +}; + +struct FileHandle +{ + int opened; + int mode; + char *name; +}; + +struct DirHandle +{ + int opened; + /* Current count of entries left */ + int count; + /* Current position in the directory entries */ + int pos; + /* Head of list, each entry will be freed when read */ + SceIoDirent *pDir; +}; + +struct FileHandle open_files[MAX_FILES]; +struct DirHandle open_dirs[MAX_DIRS]; + +static usb_dev_handle *g_hDev = NULL; + +static int g_servsocks[MAX_ASYNC_CHANNELS]; +static int g_clientsocks[MAX_ASYNC_CHANNELS]; +static const char *g_mapfile = NULL; + +pthread_mutex_t g_drivemtx = PTHREAD_MUTEX_INITIALIZER; +struct HostDrive g_drives[MAX_HOSTDRIVES]; +char g_rootdir[PATH_MAX]; + +int g_verbose = 0; +int g_gdbdebug = 0; +int g_nocase = 0; +int g_msslash = 0; +int g_pid = HOSTFSDRIVER_PID; +int g_timeout = USB_TIMEOUT; +int g_globalbind = 0; +int g_daemon = 0; +unsigned short g_baseport = BASE_PORT; + +#define V_PRINTF(level, fmt, ...) { if(g_verbose >= level) { fprintf(stderr, fmt, ## __VA_ARGS__); } } + +#if defined BUILD_BIGENDIAN || defined _BIG_ENDIAN +uint16_t swap16(uint16_t i) +{ + uint8_t *p = (uint8_t *) &i; + uint16_t ret; + + ret = (p[1] << 8) | p[0]; + + return ret; +} + +uint32_t swap32(uint32_t i) +{ + uint8_t *p = (uint8_t *) &i; + uint32_t ret; + + ret = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; + + return ret; +} + +uint64_t swap64(uint64_t i) +{ + uint8_t *p = (uint8_t *) &i; + uint64_t ret; + + ret = (uint64_t) p[0] | ((uint64_t) p[1] << 8) | ((uint64_t) p[2] << 16) | ((uint64_t) p[3] << 24) + | ((uint64_t) p[4] << 32) | ((uint64_t) p[5] << 40) | ((uint64_t) p[6] << 48) | ((uint64_t) p[7] << 56); + + return ret; +} +#define LE16(x) swap16(x) +#define LE32(x) swap32(x) +#define LE64(x) swap64(x) +#else +#define LE16(x) (x) +#define LE32(x) (x) +#define LE64(x) (x) +#endif + +#define GETERROR(x) (0x80010000 | (x)) + +void print_gdbdebug(int dir, const uint8_t *data, int len) +{ + int i; + + if(dir) + { + printf("HOST->GDB ("); + } + else + { + printf("GDB->HOST ("); + } + + for(i = 0; i < len; i++) + { + if(data[i] >= 32) + { + putchar(data[i]); + } + else + { + printf("\\%02x", data[i]); + } + } + + printf(")\n"); +} + +int is_dir(const char *path) +{ + struct stat s; + int ret = 0; + + if(!stat(path, &s)) + { + if(S_ISDIR(s.st_mode)) + { + ret = 1; + } + } + + return ret; +} + +/* Define wrappers for the usb functions we use which can set euid */ +int euid_usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout) +{ + int ret; + + V_PRINTF(2, "Bulk Write dev %p, ep 0x%x, bytes %p, size %d, timeout %d\n", + dev, ep, bytes, size, timeout); + + seteuid(0); + setegid(0); + ret = usb_bulk_write(dev, ep, bytes, size, timeout); + seteuid(getuid()); + setegid(getgid()); + + V_PRINTF(2, "Bulk Write returned %d\n", ret); + + return ret; +} + +int euid_usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout) +{ + int ret; + + V_PRINTF(2, "Bulk Read dev %p, ep 0x%x, bytes %p, size %d, timeout %d\n", + dev, ep, bytes, size, timeout); + seteuid(0); + setegid(0); + ret = usb_bulk_read(dev, ep, bytes, size, timeout); + seteuid(getuid()); + setegid(getgid()); + + V_PRINTF(2, "Bulk Read returned %d\n", ret); + + return ret; +} + +usb_dev_handle *open_device(struct usb_bus *busses) +{ + struct usb_bus *bus = NULL; + struct usb_dev_handle *hDev = NULL; + + seteuid(0); + setegid(0); + + for(bus = busses; bus; bus = bus->next) + { + struct usb_device *dev; + + for(dev = bus->devices; dev; dev = dev->next) + { + if((dev->descriptor.idVendor == SONY_VID) + && (dev->descriptor.idProduct == g_pid)) + { + hDev = usb_open(dev); + if(hDev != NULL) + { + int ret; + ret = usb_set_configuration(hDev, 1); +#ifndef NO_UID_CHECK + if((ret < 0) && (errno == EPERM) && geteuid()) { + fprintf(stderr, + "Permission error while opening the USB device.\n" + "Fix device permissions or run as root.\n"); + usb_close(hDev); + exit(1); + } +#endif + if(ret == 0) + { + ret = usb_claim_interface(hDev, 0); + if(ret == 0) + { + seteuid(getuid()); + setegid(getgid()); + return hDev; + } + else + { + usb_close(hDev); + hDev = NULL; + } + } + else + { + usb_close(hDev); + hDev = NULL; + } + } + } + } + } + + if(hDev) + { + usb_close(hDev); + } + + seteuid(getuid()); + setegid(getgid()); + + return NULL; +} + +void close_device(struct usb_dev_handle *hDev) +{ + seteuid(0); + setegid(0); + if(hDev) + { + usb_release_interface(hDev, 0); + usb_reset(hDev); + usb_close(hDev); + } + seteuid(getuid()); + setegid(getgid()); +} + +int gen_path(char *path, int dir) +{ + char abspath[PATH_MAX]; + const char *tokens[MAX_TOKENS]; + const char *outtokens[MAX_TOKENS]; + int count; + int token; + int pathpos; + + strcpy(abspath, path); + count = 0; + tokens[0] = strtok(abspath, "/"); + while((tokens[count]) && (count < (MAX_TOKENS-1))) + { + tokens[++count] = strtok(NULL, "/"); + } + + /* Remove any single . and .. */ + pathpos = 0; + for(token = 0; token < count; token++) + { + if(strcmp(tokens[token], ".") == 0) + { + /* Do nothing */ + } + else if(strcmp(tokens[token], "..") == 0) + { + /* Decrement the path position if > 0 */ + if(pathpos > 0) + { + pathpos--; + } + } + else + { + outtokens[pathpos++] = tokens[token]; + } + } + + strcpy(path, "/"); + for(token = 0; token < pathpos; token++) + { + strcat(path, outtokens[token]); + if((dir) || (token < (pathpos-1))) + { + strcat(path, "/"); + } + } + + return 1; +} + +int calc_rating(const char *str1, const char *str2) +{ + int rating = 0; + + while((*str1) && (*str2)) + { + if(*str1 == *str2) + { + rating++; + } + str1++; + str2++; + } + + return rating; +} + +/* Scan the directory, return the first name which matches case insensitive */ +int find_nocase(const char *rootdir, const char *relpath, char *token) +{ + DIR *dir; + struct dirent *ent; + char abspath[PATH_MAX]; + char match[PATH_MAX]; + int len; + int rating = -1; + int ret = 0; + + V_PRINTF(2, "Finding token %s\n", token); + + len = snprintf(abspath, PATH_MAX, "%s%s", rootdir, relpath); + if((len < 0) || (len > PATH_MAX)) + { + return 0; + } + + V_PRINTF(2, "Checking %s\n", abspath); + dir = opendir(abspath); + if(dir != NULL) + { + V_PRINTF(2, "Opened directory\n"); + while((ent = readdir(dir))) + { + V_PRINTF(2, "Got dir entry %p->%s\n", ent, ent->d_name); + if(strcasecmp(ent->d_name, token) == 0) + { + int tmp; + + tmp = calc_rating(token, ent->d_name); + V_PRINTF(2, "Found match %s for %s rating %d\n", ent->d_name, token, tmp); + if(tmp > rating) + { + strcpy(match, ent->d_name); + rating = tmp; + } + + ret = 1; + } + } + + closedir(dir); + } + else + { + V_PRINTF(2, "Couldn't open %s\n", abspath); + } + + if(ret) + { + strcpy(token, match); + } + + return ret; +} + +/* Make a relative path case insensitive, if we fail then leave the path as is, just in case */ +void make_nocase(const char *rootdir, char *path, int dir) +{ + char abspath[PATH_MAX]; + char retpath[PATH_MAX]; + char *tokens[MAX_TOKENS]; + int count; + int token; + + strcpy(abspath, path); + count = 0; + tokens[0] = strtok(abspath, "/"); + while((tokens[count]) && (count < (MAX_TOKENS-1))) + { + tokens[++count] = strtok(NULL, "/"); + } + + strcpy(retpath, "/"); + for(token = 0; token < count; token++) + { + if(!find_nocase(rootdir, retpath, tokens[token])) + { + /* Might only be an error if this is not the last token, otherwise we could be + * trying to create a new directory or file, if we are not then the rest of the code + * will handle the error */ + if((token < (count-1))) + { + break; + } + } + + strcat(retpath, tokens[token]); + if((dir) || (token < (count-1))) + { + strcat(retpath, "/"); + } + } + + if(token == count) + { + strcpy(path, retpath); + } +} + +int make_path(unsigned int drive, const char *path, char *retpath, int dir) +{ + char hostpath[PATH_MAX]; + int len; + int ret = -1; + + if(drive >= MAX_HOSTDRIVES) + { + fprintf(stderr, "Host drive number is too large (%d)\n", drive); + return -1; + } + + if(pthread_mutex_lock(&g_drivemtx)) + { + fprintf(stderr, "Could not lock mutex (%s)\n", strerror(errno)); + return -1; + } + + do + { + + len = snprintf(hostpath, PATH_MAX, "%s%s", g_drives[drive].currdir, path); + if((len < 0) || (len >= PATH_MAX)) + { + fprintf(stderr, "Path length too big (%d)\n", len); + break; + } + + if(g_msslash) + { + int i; + + for(i = 0; i < len; i++) + { + if(hostpath[i] == '\\') + { + hostpath[i] = '/'; + } + } + } + + if(gen_path(hostpath, dir) == 0) + { + break; + } + + /* Make the relative path case insensitive if needed */ + if(g_nocase) + { + make_nocase(g_drives[drive].rootdir, hostpath, dir); + } + + len = snprintf(retpath, PATH_MAX, "%s/%s", g_drives[drive].rootdir, hostpath); + if((len < 0) || (len >= PATH_MAX)) + { + fprintf(stderr, "Path length too big (%d)\n", len); + break; + } + + if(gen_path(retpath, dir) == 0) + { + break; + } + + ret = 0; + } + while(0); + + pthread_mutex_unlock(&g_drivemtx); + + return ret; +} + +int open_file(int drive, const char *path, unsigned int mode, unsigned int mask) +{ + char fullpath[PATH_MAX]; + unsigned int real_mode = 0; + int fd = -1; + + if(make_path(drive, path, fullpath, 0) < 0) + { + V_PRINTF(1, "Invalid file path %s\n", path); + return GETERROR(ENOENT); + } + + V_PRINTF(2, "open: %s\n", fullpath); + V_PRINTF(1, "Opening file %s\n", fullpath); + + if((mode & PSP_O_RDWR) == PSP_O_RDWR) + { + V_PRINTF(2, "Read/Write mode\n"); + real_mode = O_RDWR; + } + else + { + if(mode & PSP_O_RDONLY) + { + V_PRINTF(2, "Read mode\n"); + real_mode = O_RDONLY; + } + else if(mode & PSP_O_WRONLY) + { + V_PRINTF(2, "Write mode\n"); + real_mode = O_WRONLY; + } + else + { + fprintf(stderr, "No access mode specified\n"); + return GETERROR(EINVAL); + } + } + + if(mode & PSP_O_APPEND) + { + real_mode |= O_APPEND; + } + + if(mode & PSP_O_CREAT) + { + real_mode |= O_CREAT; + } + + if(mode & PSP_O_TRUNC) + { + real_mode |= O_TRUNC; + } + + if(mode & PSP_O_EXCL) + { + real_mode |= O_EXCL; + } + + if(!is_dir(fullpath)) + { + fd = open(fullpath, real_mode, mask & ~0111); + if(fd >= 0) + { + if(fd < MAX_FILES) + { + open_files[fd].opened = 1; + open_files[fd].mode = mode; + open_files[fd].name = strdup(fullpath); + } + else + { + close(fd); + fprintf(stderr, "Error filedescriptor out of range\n"); + fd = GETERROR(EMFILE); + } + } + else + { + V_PRINTF(1, "Could not open file %s\n", fullpath); + fd = GETERROR(errno); + } + } + else + { + fd = GETERROR(ENOENT); + } + + return fd; +} + +void fill_time(time_t t, ScePspDateTime *scetime) +{ + struct tm *filetime; + + memset(scetime, 0, sizeof(*scetime)); + filetime = localtime(&t); + scetime->year = LE16(filetime->tm_year + 1900); + scetime->month = LE16(filetime->tm_mon + 1); + scetime->day = LE16(filetime->tm_mday); + scetime->hour = LE16(filetime->tm_hour); + scetime->minute = LE16(filetime->tm_min); + scetime->second = LE16(filetime->tm_sec); +} + +int fill_stat(const char *dirname, const char *name, SceIoStat *scestat) +{ + char path[PATH_MAX]; + struct stat st; + int len; + + /* If dirname is NULL then name is a preconverted path */ + if(dirname != NULL) + { + if(dirname[strlen(dirname)-1] == '/') + { + len = snprintf(path, PATH_MAX, "%s%s", dirname, name); + } + else + { + len = snprintf(path, PATH_MAX, "%s/%s", dirname, name); + } + if((len < 0) || (len > PATH_MAX)) + { + fprintf(stderr, "Couldn't fill in directory name\n"); + return GETERROR(ENAMETOOLONG); + } + } + else + { + strcpy(path, name); + } + + if(stat(path, &st) < 0) + { + fprintf(stderr, "Couldn't stat file %s (%s)\n", path, strerror(errno)); + return GETERROR(errno); + } + + scestat->size = LE64(st.st_size); + scestat->mode = 0; + scestat->attr = 0; + if(S_ISLNK(st.st_mode)) + { + scestat->attr = LE32(FIO_SO_IFLNK); + scestat->mode = LE32(FIO_S_IFLNK); + } + else if(S_ISDIR(st.st_mode)) + { + scestat->attr = LE32(FIO_SO_IFDIR); + scestat->mode = LE32(FIO_S_IFDIR); + } + else + { + scestat->attr = LE32(FIO_SO_IFREG); + scestat->mode = LE32(FIO_S_IFREG); + } + + scestat->mode |= LE32(st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + + fill_time(st.st_ctime, &scestat->ctime); + fill_time(st.st_atime, &scestat->atime); + fill_time(st.st_mtime, &scestat->mtime); + + return 0; +} + +int dir_open(int drive, const char *dirname) +{ + char fulldir[PATH_MAX]; + struct dirent **entries; + int ret = -1; + int i; + int did; + int dirnum; + + do + { + for(did = 0; did < MAX_DIRS; did++) + { + if(!open_dirs[did].opened) + { + break; + } + } + + if(did == MAX_DIRS) + { + fprintf(stderr, "Could not find free directory handle\n"); + ret = GETERROR(EMFILE); + break; + } + + if(make_path(drive, dirname, fulldir, 1) < 0) + { + ret = GETERROR(ENOENT); + break; + } + + V_PRINTF(2, "dopen: %s, fsnum %d\n", fulldir, drive); + V_PRINTF(1, "Opening directory %s\n", fulldir); + + memset(&open_dirs[did], 0, sizeof(open_dirs[did])); + + dirnum = scandir(fulldir, &entries, NULL, alphasort); + if(dirnum <= 0) + { + fprintf(stderr, "Could not scan directory %s (%s)\n", fulldir, strerror(errno)); + ret = GETERROR(errno); + break; + } + + V_PRINTF(2, "Number of dir entries %d\n", dirnum); + + open_dirs[did].pDir = malloc(sizeof(SceIoDirent) * dirnum); + if(open_dirs[did].pDir != NULL) + { + memset(open_dirs[did].pDir, 0, sizeof(SceIoDirent) * dirnum); + for(i = 0; i < dirnum; i++) + { + strcpy(open_dirs[did].pDir[i].name, entries[i]->d_name); + V_PRINTF(2, "Dirent %d: %s\n", i, entries[i]->d_name); + if(fill_stat(fulldir, entries[i]->d_name, &open_dirs[did].pDir[i].stat) < 0) + { + fprintf(stderr, "Error filling in directory structure\n"); + break; + } + } + + if(i == dirnum) + { + ret = did; + open_dirs[did].pos = 0; + open_dirs[did].count = dirnum; + open_dirs[did].opened = 1; + } + else + { + free(open_dirs[did].pDir); + } + } + else + { + fprintf(stderr, "Could not allocate memory for directories\n"); + } + + if(ret < 0) + { + for(i = 0; i < dirnum; i++) + { + free(entries[i]); + } + free(entries); + } + } + while(0); + + return ret; +} + +int dir_close(int did) +{ + int ret = -1; + if((did >= 0) && (did < MAX_DIRS)) + { + if(open_dirs[did].opened) + { + if(open_dirs[did].pDir) + { + free(open_dirs[did].pDir); + } + + open_dirs[did].opened = 0; + open_dirs[did].count = 0; + open_dirs[did].pos = 0; + open_dirs[did].pDir = NULL; + + ret = 0; + } + } + + return ret; +} + +int handle_hello(struct usb_dev_handle *hDev) +{ + struct HostFsHelloResp resp; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_HELLO); + + return usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); +} + +int handle_open(struct usb_dev_handle *hDev, struct HostFsOpenCmd *cmd, int cmdlen) +{ + struct HostFsOpenResp resp; + int ret = -1; + char path[HOSTFS_PATHMAX]; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_OPEN); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsOpenCmd)) + { + fprintf(stderr, "Error, invalid open command size %d\n", cmdlen); + break; + } + + if(LE32(cmd->cmd.extralen) == 0) + { + fprintf(stderr, "Error, no filename passed with open command\n"); + break; + } + + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, path, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading open data cmd->extralen %ud, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + V_PRINTF(2, "Open command mode %08X mask %08X name %s\n", LE32(cmd->mode), LE32(cmd->mask), path); + resp.res = LE32(open_file(LE32(cmd->fsnum), path, LE32(cmd->mode), LE32(cmd->mask))); + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int handle_dopen(struct usb_dev_handle *hDev, struct HostFsDopenCmd *cmd, int cmdlen) +{ + struct HostFsDopenResp resp; + int ret = -1; + char path[HOSTFS_PATHMAX]; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_DOPEN); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsDopenCmd)) + { + fprintf(stderr, "Error, invalid dopen command size %d\n", cmdlen); + break; + } + + if(LE32(cmd->cmd.extralen) == 0) + { + fprintf(stderr, "Error, no dirname passed with dopen command\n"); + break; + } + + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, path, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading open data cmd->extralen %d, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + V_PRINTF(2, "Dopen command name %s\n", path); + resp.res = LE32(dir_open(LE32(cmd->fsnum), path)); + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int fixed_write(int fd, const void *data, int len) +{ + int byteswrite = 0; + while(byteswrite < len) + { + int ret; + + ret = write(fd, data+byteswrite, len-byteswrite); + if(ret < 0) + { + if(errno != EINTR) + { + fprintf(stderr, "Error writing to file (%s)\n", strerror(errno)); + byteswrite = GETERROR(errno); + break; + } + } + else if(ret == 0) /* EOF? */ + { + break; + } + else + { + byteswrite += ret; + } + } + + return byteswrite; +} + +int handle_write(struct usb_dev_handle *hDev, struct HostFsWriteCmd *cmd, int cmdlen) +{ + static char write_block[HOSTFS_MAX_BLOCK]; + struct HostFsWriteResp resp; + int fid; + int ret = -1; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_WRITE); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsWriteCmd)) + { + fprintf(stderr, "Error, invalid write command size %d\n", cmdlen); + break; + } + + /* TODO: Check upper bound */ + if(LE32(cmd->cmd.extralen) <= 0) + { + fprintf(stderr, "Error extralen invalid (%d)\n", LE32(cmd->cmd.extralen)); + break; + } + + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, write_block, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading write data cmd->extralen %d, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + fid = LE32(cmd->fid); + + V_PRINTF(2, "Write command fid: %d, length: %d\n", fid, LE32(cmd->cmd.extralen)); + + if((fid >= 0) && (fid < MAX_FILES)) + { + if(open_files[fid].opened) + { + resp.res = LE32(fixed_write(fid, write_block, LE32(cmd->cmd.extralen))); + } + else + { + fprintf(stderr, "Error fid not open %d\n", fid); + } + } + else + { + fprintf(stderr, "Error invalid fid %d\n", fid); + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int fixed_read(int fd, void *data, int len) +{ + int bytesread = 0; + + while(bytesread < len) + { + int ret; + + ret = read(fd, data+bytesread, len-bytesread); + if(ret < 0) + { + if(errno != EINTR) + { + bytesread = GETERROR(errno); + break; + } + } + else if(ret == 0) + { + /* No more to read */ + break; + } + else + { + bytesread += ret; + } + } + + return bytesread; +} + +int handle_read(struct usb_dev_handle *hDev, struct HostFsReadCmd *cmd, int cmdlen) +{ + static char read_block[HOSTFS_MAX_BLOCK]; + struct HostFsReadResp resp; + int fid; + int ret = -1; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_READ); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsReadCmd)) + { + fprintf(stderr, "Error, invalid read command size %d\n", cmdlen); + break; + } + + /* TODO: Check upper bound */ + if(LE32(cmd->len) <= 0) + { + fprintf(stderr, "Error extralen invalid (%d)\n", LE32(cmd->len)); + break; + } + + fid = LE32(cmd->fid); + V_PRINTF(2, "Read command fid: %d, length: %d\n", fid, LE32(cmd->len)); + + if((fid >= 0) && (fid < MAX_FILES)) + { + if(open_files[fid].opened) + { + resp.res = LE32(fixed_read(fid, read_block, LE32(cmd->len))); + if(LE32(resp.res) >= 0) + { + resp.cmd.extralen = resp.res; + } + } + else + { + fprintf(stderr, "Error fid not open %d\n", fid); + } + } + else + { + fprintf(stderr, "Error invalid fid %d\n", fid); + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + if(ret < 0) + { + fprintf(stderr, "Error writing read response (%d)\n", ret); + break; + } + + if(LE32(resp.cmd.extralen) > 0) + { + ret = euid_usb_bulk_write(hDev, 0x2, read_block, LE32(resp.cmd.extralen), 10000); + } + } + while(0); + + return ret; +} + +int handle_close(struct usb_dev_handle *hDev, struct HostFsCloseCmd *cmd, int cmdlen) +{ + struct HostFsCloseResp resp; + int ret = -1; + int fid; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_CLOSE); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsCloseCmd)) + { + fprintf(stderr, "Error, invalid close command size %d\n", cmdlen); + break; + } + + fid = LE32(cmd->fid); + V_PRINTF(2, "Close command fid: %d\n", fid); + if((fid > STDERR_FILENO) && (fid < MAX_FILES) && (open_files[fid].opened)) + { + if(close(fid) < 0) + { + resp.res = LE32(GETERROR(errno)); + } + else + { + resp.res = LE32(0); + } + + open_files[fid].opened = 0; + if(open_files[fid].name) + { + free(open_files[fid].name); + open_files[fid].name = NULL; + } + } + else + { + fprintf(stderr, "Error invalid file id in close command (%d)\n", fid); + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int handle_dclose(struct usb_dev_handle *hDev, struct HostFsDcloseCmd *cmd, int cmdlen) +{ + struct HostFsDcloseResp resp; + int ret = -1; + int did; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_DCLOSE); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsDcloseCmd)) + { + fprintf(stderr, "Error, invalid close command size %d\n", cmdlen); + break; + } + + did = LE32(cmd->did); + V_PRINTF(2, "Dclose command did: %d\n", did); + resp.res = dir_close(did); + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + + return ret; +} + +int handle_dread(struct usb_dev_handle *hDev, struct HostFsDreadCmd *cmd, int cmdlen) +{ + struct HostFsDreadResp resp; + SceIoDirent *dir = NULL; + int ret = -1; + int did; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_READ); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsDreadCmd)) + { + fprintf(stderr, "Error, invalid dread command size %d\n", cmdlen); + break; + } + + did = LE32(cmd->did); + V_PRINTF(2, "Dread command did: %d\n", did); + + if((did >= 0) && (did < MAX_FILES)) + { + if(open_dirs[did].opened) + { + if(open_dirs[did].pos < open_dirs[did].count) + { + dir = &open_dirs[did].pDir[open_dirs[did].pos++]; + resp.cmd.extralen = LE32(sizeof(SceIoDirent)); + resp.res = LE32(open_dirs[did].count - open_dirs[did].pos + 1); + } + else + { + resp.res = LE32(0); + } + } + else + { + fprintf(stderr, "Error did not open %d\n", did); + } + } + else + { + fprintf(stderr, "Error invalid did %d\n", did); + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + if(ret < 0) + { + fprintf(stderr, "Error writing dread response (%d)\n", ret); + break; + } + + if(LE32(resp.cmd.extralen) > 0) + { + ret = euid_usb_bulk_write(hDev, 0x2, (char *) dir, LE32(resp.cmd.extralen), 10000); + } + } + while(0); + + return ret; +} + +int handle_lseek(struct usb_dev_handle *hDev, struct HostFsLseekCmd *cmd, int cmdlen) +{ + struct HostFsLseekResp resp; + int ret = -1; + int fid; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_LSEEK); + resp.res = LE32(-1); + resp.ofs = LE32(0); + + do + { + if(cmdlen != sizeof(struct HostFsLseekCmd)) + { + fprintf(stderr, "Error, invalid lseek command size %d\n", cmdlen); + break; + } + + fid = LE32(cmd->fid); + V_PRINTF(2, "Lseek command fid: %d, ofs: %lld, whence: %d\n", fid, LE64(cmd->ofs), LE32(cmd->whence)); + if((fid > STDERR_FILENO) && (fid < MAX_FILES) && (open_files[fid].opened)) + { + /* TODO: Probably should ensure whence is mapped across, just in case */ + resp.ofs = LE64((int64_t) lseek(fid, (off_t) LE64(cmd->ofs), LE32(cmd->whence))); + if(LE64(resp.ofs) < 0) + { + resp.res = LE32(-1); + } + else + { + resp.res = LE32(0); + } + } + else + { + fprintf(stderr, "Error invalid file id in close command (%d)\n", fid); + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int handle_remove(struct usb_dev_handle *hDev, struct HostFsRemoveCmd *cmd, int cmdlen) +{ + struct HostFsRemoveResp resp; + int ret = -1; + char path[HOSTFS_PATHMAX]; + char fullpath[PATH_MAX]; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_REMOVE); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsRemoveCmd)) + { + fprintf(stderr, "Error, invalid remove command size %d\n", cmdlen); + break; + } + + if(LE32(cmd->cmd.extralen) == 0) + { + fprintf(stderr, "Error, no filename passed with remove command\n"); + break; + } + + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, path, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading remove data cmd->extralen %d, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + V_PRINTF(2, "Remove command name %s\n", path); + if(make_path(LE32(cmd->fsnum), path, fullpath, 0) == 0) + { + if(unlink(fullpath) < 0) + { + resp.res = LE32(GETERROR(errno)); + } + else + { + resp.res = LE32(0); + } + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int handle_rmdir(struct usb_dev_handle *hDev, struct HostFsRmdirCmd *cmd, int cmdlen) +{ + struct HostFsRmdirResp resp; + int ret = -1; + char path[HOSTFS_PATHMAX]; + char fullpath[PATH_MAX]; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_RMDIR); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsRmdirCmd)) + { + fprintf(stderr, "Error, invalid rmdir command size %d\n", cmdlen); + break; + } + + if(LE32(cmd->cmd.extralen) == 0) + { + fprintf(stderr, "Error, no filename passed with rmdir command\n"); + break; + } + + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, path, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading rmdir data cmd->extralen %d, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + V_PRINTF(2, "Rmdir command name %s\n", path); + if(make_path(LE32(cmd->fsnum), path, fullpath, 0) == 0) + { + if(rmdir(fullpath) < 0) + { + resp.res = LE32(GETERROR(errno)); + } + else + { + resp.res = LE32(0); + } + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int handle_mkdir(struct usb_dev_handle *hDev, struct HostFsMkdirCmd *cmd, int cmdlen) +{ + struct HostFsMkdirResp resp; + int ret = -1; + char path[HOSTFS_PATHMAX]; + char fullpath[PATH_MAX]; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_MKDIR); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsMkdirCmd)) + { + fprintf(stderr, "Error, invalid mkdir command size %d\n", cmdlen); + break; + } + + if(LE32(cmd->cmd.extralen) == 0) + { + fprintf(stderr, "Error, no filename passed with mkdir command\n"); + break; + } + + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, path, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading mkdir data cmd->extralen %d, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + V_PRINTF(2, "Mkdir command mode %08X, name %s\n", LE32(cmd->mode), path); + if(make_path(LE32(cmd->fsnum), path, fullpath, 0) == 0) + { + if(mkdir(fullpath, LE32(cmd->mode)) < 0) + { + resp.res = LE32(GETERROR(errno)); + } + else + { + resp.res = LE32(0); + } + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int handle_getstat(struct usb_dev_handle *hDev, struct HostFsGetstatCmd *cmd, int cmdlen) +{ + struct HostFsGetstatResp resp; + SceIoStat st; + int ret = -1; + char path[HOSTFS_PATHMAX]; + char fullpath[PATH_MAX]; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_GETSTAT); + resp.res = LE32(-1); + memset(&st, 0, sizeof(st)); + + do + { + if(cmdlen != sizeof(struct HostFsGetstatCmd)) + { + fprintf(stderr, "Error, invalid getstat command size %d\n", cmdlen); + break; + } + + if(LE32(cmd->cmd.extralen) == 0) + { + fprintf(stderr, "Error, no filename passed with getstat command\n"); + break; + } + + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, path, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading getstat data cmd->extralen %d, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + V_PRINTF(2, "Getstat command name %s\n", path); + if(make_path(LE32(cmd->fsnum), path, fullpath, 0) == 0) + { + resp.res = LE32(fill_stat(NULL, fullpath, &st)); + if(LE32(resp.res) == 0) + { + resp.cmd.extralen = LE32(sizeof(st)); + } + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + if(ret < 0) + { + fprintf(stderr, "Error writing getstat response (%d)\n", ret); + break; + } + + if(LE32(resp.cmd.extralen) > 0) + { + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &st, sizeof(st), 10000); + } + } + while(0); + + return ret; +} + +int psp_settime(const char *path, const struct HostFsTimeStamp *ts, int set) +{ + time_t convtime; + struct tm stime; + struct utimbuf tbuf; + struct stat st; + + stime.tm_year = LE16(ts->year) - 1900; + stime.tm_mon = LE16(ts->month) - 1; + stime.tm_mday = LE16(ts->day); + stime.tm_hour = LE16(ts->hour); + stime.tm_min = LE16(ts->minute); + stime.tm_sec = LE16(ts->second); + + if(stat(path, &st) < 0) + { + return -1; + } + + tbuf.actime = st.st_atime; + tbuf.modtime = st.st_mtime; + + convtime = mktime(&stime); + if(convtime == (time_t)-1) + { + return -1; + } + + if(set == PSP_CHSTAT_ATIME) + { + tbuf.actime = convtime; + } + else if(set == PSP_CHSTAT_MTIME) + { + tbuf.modtime = convtime; + } + else + { + return -1; + } + + return utime(path, &tbuf); + +} + +int psp_chstat(const char *path, struct HostFsChstatCmd *cmd) +{ + int ret = 0; + + if(LE32(cmd->bits) & PSP_CHSTAT_MODE) + { + int mask; + + mask = LE32(cmd->mode) & (FIO_S_IRWXU | FIO_S_IRWXG | FIO_S_IRWXO); + ret = chmod(path, mask); + if(ret < 0) + { + V_PRINTF(2, "Could not set file mask\n"); + return GETERROR(errno); + } + } + + if(LE32(cmd->bits) & PSP_CHSTAT_SIZE) + { + /* Do a truncate */ + } + + if(LE32(cmd->bits) & PSP_CHSTAT_ATIME) + { + if(psp_settime(path, &cmd->atime, PSP_CHSTAT_ATIME) < 0) + { + V_PRINTF(2, "Could not set access time\n"); + return -1; + } + } + + if(LE32(cmd->bits) & PSP_CHSTAT_MTIME) + { + if(psp_settime(path, &cmd->mtime, PSP_CHSTAT_MTIME) < 0) + { + V_PRINTF(2, "Could not set modification time\n"); + return -1; + } + } + + return 0; +} + +int handle_chstat(struct usb_dev_handle *hDev, struct HostFsChstatCmd *cmd, int cmdlen) +{ + struct HostFsChstatResp resp; + int ret = -1; + char path[HOSTFS_PATHMAX]; + char fullpath[PATH_MAX]; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_CHSTAT); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsChstatCmd)) + { + fprintf(stderr, "Error, invalid chstat command size %d\n", cmdlen); + break; + } + + if(LE32(cmd->cmd.extralen) == 0) + { + fprintf(stderr, "Error, no filename passed with chstat command\n"); + break; + } + + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, path, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading chstat data cmd->extralen %d, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + V_PRINTF(2, "Chstat command name %s, bits %08X\n", path, LE32(cmd->bits)); + if(make_path(LE32(cmd->fsnum), path, fullpath, 0) == 0) + { + resp.res = LE32(psp_chstat(fullpath, cmd)); + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int handle_rename(struct usb_dev_handle *hDev, struct HostFsRenameCmd *cmd, int cmdlen) +{ + struct HostFsRenameResp resp; + int ret = -1; + char path[HOSTFS_PATHMAX]; + char oldpath[PATH_MAX]; + char newpath[PATH_MAX]; + char destpath[PATH_MAX]; + int oldpathlen; + int newpathlen; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_RENAME); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsRenameCmd)) + { + fprintf(stderr, "Error, invalid rename command size %d\n", cmdlen); + break; + } + + if(LE32(cmd->cmd.extralen) == 0) + { + fprintf(stderr, "Error, no filenames passed with rename command\n"); + break; + } + + /* TODO: Should check that length is within a valid range */ + + memset(path, 0, sizeof(path)); + ret = euid_usb_bulk_read(hDev, 0x81, path, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading rename data cmd->extralen %d, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + /* Really should check this better ;) */ + oldpathlen = strlen(path); + newpathlen = strlen(path+oldpathlen+1); + + /* If the old path is absolute and the new path is relative then rebase newpath */ + if((*path == '/') && (*(path+oldpathlen+1) != '/')) + { + char *slash; + + strcpy(destpath, path); + /* No need to check, should at least stop on the first slash */ + slash = strrchr(destpath, '/'); + /* Nul terminate after slash */ + *(slash+1) = 0; + strcat(destpath, path+oldpathlen+1); + } + else + { + /* Just copy in oldpath */ + strcpy(destpath, path+oldpathlen+1); + } + + V_PRINTF(2, "Rename command oldname %s, newname %s\n", path, destpath); + + if(!make_path(LE32(cmd->fsnum), path, oldpath, 0) && !make_path(LE32(cmd->fsnum), destpath, newpath, 0)) + { + if(rename(oldpath, newpath) < 0) + { + resp.res = LE32(GETERROR(errno)); + } + else + { + resp.res = LE32(0); + } + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int handle_chdir(struct usb_dev_handle *hDev, struct HostFsChdirCmd *cmd, int cmdlen) +{ + struct HostFsChdirResp resp; + int ret = -1; + char path[HOSTFS_PATHMAX]; + int fsnum; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_CHDIR); + resp.res = -1; + + do + { + if(cmdlen != sizeof(struct HostFsChdirCmd)) + { + fprintf(stderr, "Error, invalid chdir command size %d\n", cmdlen); + break; + } + + if(LE32(cmd->cmd.extralen) == 0) + { + fprintf(stderr, "Error, no filename passed with mkdir command\n"); + break; + } + + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, path, LE32(cmd->cmd.extralen), 10000); + if(ret != LE32(cmd->cmd.extralen)) + { + fprintf(stderr, "Error reading chdir data cmd->extralen %d, ret %d\n", LE32(cmd->cmd.extralen), ret); + break; + } + + V_PRINTF(2, "Chdir command name %s\n", path); + + fsnum = LE32(cmd->fsnum); + if((fsnum >= 0) && (fsnum < MAX_HOSTDRIVES)) + { + strcpy(g_drives[fsnum].currdir, path); + resp.res = 0; + } + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + } + while(0); + + return ret; +} + +int handle_ioctl(struct usb_dev_handle *hDev, struct HostFsIoctlCmd *cmd, int cmdlen) +{ + static char inbuf[64*1024]; + static char outbuf[64*1024]; + int inlen; + struct HostFsIoctlResp resp; + int ret = -1; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_IOCTL); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsIoctlCmd)) + { + fprintf(stderr, "Error, invalid ioctl command size %d\n", cmdlen); + break; + } + + inlen = LE32(cmd->cmd.extralen); + if(inlen > 0) + { + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, inbuf, inlen, 10000); + if(ret != inlen) + { + fprintf(stderr, "Error reading ioctl data cmd->extralen %d, ret %d\n", inlen, ret); + break; + } + } + + V_PRINTF(2, "Ioctl command fid %d, cmdno %d, inlen %d\n", LE32(cmd->fid), LE32(cmd->cmdno), inlen); + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + if(ret < 0) + { + fprintf(stderr, "Error writing ioctl response (%d)\n", ret); + break; + } + + if(LE32(resp.cmd.extralen) > 0) + { + ret = euid_usb_bulk_write(hDev, 0x2, (char *) outbuf, LE32(resp.cmd.extralen), 10000); + } + } + while(0); + + return ret; +} + +int get_drive_info(struct DevctlGetInfo *info, unsigned int drive) +{ + int ret = -1; + + if(drive >= MAX_HOSTDRIVES) + { + fprintf(stderr, "Host drive number is too large (%d)\n", drive); + return -1; + } + + if(pthread_mutex_lock(&g_drivemtx)) + { + fprintf(stderr, "Could not lock mutex (%s)\n", strerror(errno)); + return -1; + } + + do + { +#ifdef __CYGWIN__ + struct statfs st; + + if(statfs(g_drives[drive].rootdir, &st) < 0) + { + fprintf(stderr, "Could not stat %s (%s)\n", g_drives[drive].rootdir, strerror(errno)); + break; + } + + info->btotal = st.f_blocks; + info->bfree = st.f_bfree; + info->unk = st.f_blocks; + info->ssize = 512; + info->sects = st.f_bsize / 512; + + memset(info, 0, sizeof(struct DevctlGetInfo)); +#else + struct statvfs st; + + if(statvfs(g_drives[drive].rootdir, &st) < 0) + { + fprintf(stderr, "Could not stat %s (%s)\n", g_drives[drive].rootdir, strerror(errno)); + break; + } + + info->btotal = st.f_blocks; + info->bfree = st.f_bfree; + info->unk = st.f_blocks; + info->ssize = 512; + info->sects = st.f_frsize / 512; +#endif + + ret = 0; + } + while(0); + + pthread_mutex_unlock(&g_drivemtx); + + return ret; +} + +int handle_devctl(struct usb_dev_handle *hDev, struct HostFsDevctlCmd *cmd, int cmdlen) +{ + static char inbuf[64*1024]; + static char outbuf[64*1024]; + int inlen; + struct HostFsDevctlResp resp; + int ret = -1; + unsigned int cmdno; + + memset(&resp, 0, sizeof(resp)); + resp.cmd.magic = LE32(HOSTFS_MAGIC); + resp.cmd.command = LE32(HOSTFS_CMD_DEVCTL); + resp.res = LE32(-1); + + do + { + if(cmdlen != sizeof(struct HostFsDevctlCmd)) + { + fprintf(stderr, "Error, invalid devctl command size %d\n", cmdlen); + break; + } + + inlen = LE32(cmd->cmd.extralen); + if(inlen > 0) + { + /* TODO: Should check that length is within a valid range */ + + ret = euid_usb_bulk_read(hDev, 0x81, inbuf, inlen, 10000); + if(ret != inlen) + { + fprintf(stderr, "Error reading devctl data cmd->extralen %d, ret %d\n", inlen, ret); + break; + } + } + + cmdno = LE32(cmd->cmdno); + V_PRINTF(2, "Devctl command cmdno %d, inlen %d\n", cmdno, inlen); + + switch(cmdno) + { + case DEVCTL_GET_INFO: resp.res = LE32(get_drive_info((struct DevctlGetInfo *) outbuf, LE32(cmd->fsnum))); + if(LE32(resp.res) == 0) + { + resp.cmd.extralen = LE32(sizeof(struct DevctlGetInfo)); + } + break; + default: break; + }; + + ret = euid_usb_bulk_write(hDev, 0x2, (char *) &resp, sizeof(resp), 10000); + if(ret < 0) + { + fprintf(stderr, "Error writing devctl response (%d)\n", ret); + break; + } + + if(LE32(resp.cmd.extralen) > 0) + { + ret = euid_usb_bulk_write(hDev, 0x2, (char *) outbuf, LE32(resp.cmd.extralen), 10000); + } + } + while(0); + + return ret; +} + +usb_dev_handle *wait_for_device(void) +{ + usb_dev_handle *hDev = NULL; + + while(hDev == NULL) + { + usb_find_busses(); + usb_find_devices(); + + hDev = open_device(usb_get_busses()); + if(hDev) + { + fprintf(stderr, "Connected to device\n"); + break; + } + + /* Sleep for one second */ + sleep(1); + } + + return hDev; +} + +int init_hostfs(void) +{ + int i; + + memset(open_files, 0, sizeof(open_files)); + memset(open_dirs, 0, sizeof(open_dirs)); + for(i = 0; i < MAX_HOSTDRIVES; i++) + { + strcpy(g_drives[i].currdir, "/"); + } + + return 0; +} + +void close_hostfs(void) +{ + int i; + + for(i = 3; i < MAX_FILES; i++) + { + if(open_files[i].opened) + { + close(i); + open_files[i].opened = 0; + if(open_files[i].name) + { + free(open_files[i].name); + open_files[i].name = NULL; + } + } + } + + for(i = 0; i < MAX_DIRS; i++) + { + if(open_dirs[i].opened) + { + dir_close(i); + } + } +} + +void do_hostfs(struct HostFsCmd *cmd, int readlen) +{ + V_PRINTF(2, "Magic: %08X\n", LE32(cmd->magic)); + V_PRINTF(2, "Command Num: %08X\n", LE32(cmd->command)); + V_PRINTF(2, "Extra Len: %d\n", LE32(cmd->extralen)); + + switch(LE32(cmd->command)) + { + case HOSTFS_CMD_HELLO: if(handle_hello(g_hDev) < 0) + { + fprintf(stderr, "Error sending hello response\n"); + } + break; + case HOSTFS_CMD_OPEN: if(handle_open(g_hDev, (struct HostFsOpenCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in open command\n"); + } + break; + case HOSTFS_CMD_CLOSE: if(handle_close(g_hDev, (struct HostFsCloseCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in close command\n"); + } + break; + case HOSTFS_CMD_WRITE: if(handle_write(g_hDev, (struct HostFsWriteCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in write command\n"); + } + break; + case HOSTFS_CMD_READ: if(handle_read(g_hDev, (struct HostFsReadCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in read command\n"); + } + break; + case HOSTFS_CMD_LSEEK: if(handle_lseek(g_hDev, (struct HostFsLseekCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in lseek command\n"); + } + break; + case HOSTFS_CMD_DOPEN: if(handle_dopen(g_hDev, (struct HostFsDopenCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in dopen command\n"); + } + break; + case HOSTFS_CMD_DCLOSE: if(handle_dclose(g_hDev, (struct HostFsDcloseCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in dclose command\n"); + } + break; + case HOSTFS_CMD_DREAD: if(handle_dread(g_hDev, (struct HostFsDreadCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in dread command\n"); + } + break; + case HOSTFS_CMD_REMOVE: if(handle_remove(g_hDev, (struct HostFsRemoveCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in remove command\n"); + } + break; + case HOSTFS_CMD_RMDIR: if(handle_rmdir(g_hDev, (struct HostFsRmdirCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in rmdir command\n"); + } + break; + case HOSTFS_CMD_MKDIR: if(handle_mkdir(g_hDev, (struct HostFsMkdirCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in mkdir command\n"); + } + break; + case HOSTFS_CMD_CHDIR: if(handle_chdir(g_hDev, (struct HostFsChdirCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in chdir command\n"); + } + break; + case HOSTFS_CMD_RENAME: if(handle_rename(g_hDev, (struct HostFsRenameCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in rename command\n"); + } + break; + case HOSTFS_CMD_GETSTAT:if(handle_getstat(g_hDev, (struct HostFsGetstatCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in getstat command\n"); + } + break; + case HOSTFS_CMD_CHSTAT: if(handle_chstat(g_hDev, (struct HostFsChstatCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in chstat command\n"); + } + break; + case HOSTFS_CMD_IOCTL: if(handle_ioctl(g_hDev, (struct HostFsIoctlCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in ioctl command\n"); + } + break; + case HOSTFS_CMD_DEVCTL: if(handle_devctl(g_hDev, (struct HostFsDevctlCmd *) cmd, readlen) < 0) + { + fprintf(stderr, "Error in devctl command\n"); + } + break; + default: fprintf(stderr, "Error, unknown command %08X\n", cmd->command); + break; + }; +} + + +void do_async(struct AsyncCommand *cmd, int readlen) +{ + uint8_t *data; + + V_PRINTF(2, "Async Magic: %08X\n", LE32(cmd->magic)); + V_PRINTF(2, "Async Channel: %08X\n", LE32(cmd->channel)); + V_PRINTF(2, "Async Extra Len: %d\n", readlen - sizeof(struct AsyncCommand)); + + if(readlen > sizeof(struct AsyncCommand)) + { + data = (uint8_t *) cmd + sizeof(struct AsyncCommand); + unsigned int chan = LE32(cmd->channel); + if((chan < MAX_ASYNC_CHANNELS) && (g_clientsocks[chan] >= 0)) + { + write(g_clientsocks[chan], data, readlen - sizeof(struct AsyncCommand)); + if((chan == ASYNC_GDB) && (g_gdbdebug)) + { + print_gdbdebug(0, data, readlen - sizeof(struct AsyncCommand)); + } + } + } +} + +void do_bulk(struct BulkCommand *cmd, int readlen) +{ + static char block[HOSTFS_BULK_MAXWRITE]; + int read = 0; + int len = 0; + unsigned int chan = 0; + int ret = -1; + + chan = LE32(cmd->channel); + len = LE32(cmd->size); + + V_PRINTF(2, "Bulk write command length: %d channel %d\n", len, chan); + + while(read < len) + { + int readsize; + + readsize = (len - read) > HOSTFS_MAX_BLOCK ? HOSTFS_MAX_BLOCK : (len - read); + ret = euid_usb_bulk_read(g_hDev, 0x81, &block[read], readsize, 10000); + if(ret != readsize) + { + fprintf(stderr, "Error reading write data readsize %d, ret %d\n", readsize, ret); + break; + } + read += readsize; + } + + if(read >= len) + { + if((chan < MAX_ASYNC_CHANNELS) && (g_clientsocks[chan] >= 0)) + { + fixed_write(g_clientsocks[chan], block, len); + } + } +} + +int start_hostfs(void) +{ + uint32_t data[512/sizeof(uint32_t)]; + int readlen; + + while(1) + { + init_hostfs(); + + g_hDev = wait_for_device(); + + if(g_hDev) + { + uint32_t magic; + + magic = LE32(HOSTFS_MAGIC); + + if(euid_usb_bulk_write(g_hDev, 0x2, (char *) &magic, sizeof(magic), 1000) == sizeof(magic)) + { + while(1) + { + readlen = euid_usb_bulk_read(g_hDev, 0x81, (char*) data, 512, g_timeout); + if(readlen == 0) + { + fprintf(stderr, "Read cancelled (remote disconnected)\n"); + break; + } + else if(readlen == -ETIMEDOUT) + { + continue; + } + else if(readlen < 0) + { + break; + } + + if(readlen < sizeof(uint32_t)) + { + fprintf(stderr, "Error could not read magic\n"); + break; + } + + if(LE32(data[0]) == HOSTFS_MAGIC) + { + if(readlen < sizeof(struct HostFsCmd)) + { + fprintf(stderr, "Error reading command header %d\n", readlen); + break; + } + + do_hostfs((struct HostFsCmd *) data, readlen); + } + else if(LE32(data[0]) == ASYNC_MAGIC) + { + if(readlen < sizeof(struct AsyncCommand)) + { + fprintf(stderr, "Error reading async header %d\n", readlen); + break; + } + + do_async((struct AsyncCommand *) data, readlen); + } + else if(LE32(data[0]) == BULK_MAGIC) + { + if(readlen < sizeof(struct BulkCommand)) + { + fprintf(stderr, "Error reading bulk header %d\n", readlen); + break; + } + + do_bulk((struct BulkCommand *) data, readlen); + } + else + { + fprintf(stderr, "Error, invalid magic %08X\n", LE32(data[0])); + } + } + } + + close_device(g_hDev); + g_hDev = NULL; + } + + close_hostfs(); + } + + return 0; +} + +int parse_args(int argc, char **argv) +{ + int i; + + if(getcwd(g_rootdir, PATH_MAX) < 0) + { + fprintf(stderr, "Could not get current path\n"); + return 0; + } + + for(i = 0; i < MAX_HOSTDRIVES; i++) + { + strcpy(g_drives[i].rootdir, g_rootdir); + } + + while(1) + { + int ch; + + ch = getopt(argc, argv, "vghndcmb:p:f:t:"); + if(ch == -1) + { + break; + } + + switch(ch) + { + case 'v': g_verbose++; + break; + case 'b': g_baseport = atoi(optarg); + break; + case 'g': g_globalbind = 1; + break; + case 'p': g_pid = strtoul(optarg, NULL, 0); + break; + case 'd': g_gdbdebug = 1; + break; + case 'c': g_nocase = 1; + break; + case 'm': g_msslash = 1; + break; + case 'f': g_mapfile = optarg; + break; + case 't': g_timeout = atoi(optarg); + break; + case 'n': g_daemon = 1; + break; + case 'h': return 0; + default: printf("Unknown option\n"); + return 0; + break; + }; + } + + argc -= optind; + argv += optind; + + if(argc > 0) + { + if(argc > MAX_HOSTDRIVES) + { + argc = MAX_HOSTDRIVES; + } + + for(i = 0; i < argc; i++) + { + if(argv[i][0] != '/') + { + char tmpdir[PATH_MAX]; + snprintf(tmpdir, PATH_MAX, "%s/%s", g_rootdir, argv[i]); + strcpy(g_drives[i].rootdir, tmpdir); + } + else + { + strcpy(g_drives[i].rootdir, argv[i]); + } + gen_path(g_drives[i].rootdir, 0); + V_PRINTF(2, "Root %d: %s\n", i, g_drives[i].rootdir); + } + } + else + { + V_PRINTF(2, "Root directory: %s\n", g_rootdir); + } + + return 1; +} + +void print_help(void) +{ + fprintf(stderr, "Usage: usbhostfs_pc [options] [rootdir0..rootdir%d]\n", MAX_HOSTDRIVES-1); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "-v : Set verbose mode\n"); + fprintf(stderr, "-vv : More verbose\n"); + fprintf(stderr, "-b port : Specify the base async port (default %d)\n", BASE_PORT); + fprintf(stderr, "-g : Specify global bind for the sockets, as opposed to just localhost\n"); + fprintf(stderr, "-p pid : Specify the product ID of the PSP device\n"); + fprintf(stderr, "-d : Print GDB transfers\n"); + fprintf(stderr, "-f filename : Load the host drive mappings from a file\n"); + fprintf(stderr, "-c : Enable case-insensitive filenames\n"); + fprintf(stderr, "-m : Convert backslashes to forward slashes\n"); + fprintf(stderr, "-t timeout : Specify the USB timeout (default %d)\n", USB_TIMEOUT); + fprintf(stderr, "-n : Daemon mode, the shell is accessed through pcterm\n"); + fprintf(stderr, "-h : Print this help\n"); +} + +void shutdown_socket(void) +{ + int i; + + for(i = 0; i < MAX_ASYNC_CHANNELS; i++) + { + if(g_clientsocks[i] >= 0) + { + close(g_clientsocks[i]); + g_clientsocks[i] = -1; + } + + if(g_servsocks[i] >= 0) + { + close(g_servsocks[i]); + g_servsocks[i] = -1; + } + } +} + +int exit_app(void) +{ + printf("Exiting\n"); + shutdown_socket(); + if(g_hDev) + { + /* Nuke the connection */ + seteuid(0); + setegid(0); + close_device(g_hDev); + } + exit(1); + + return 0; +} + +void signal_handler(int sig) +{ + exit_app(); +} + +int make_socket(unsigned short port) +{ + int sock; + int on = 1; + struct sockaddr_in name; + + sock = socket(PF_INET, SOCK_STREAM, 0); + if(sock < 0) + { + perror("socket"); + return -1; + } + + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = htons(port); + if(g_globalbind) + { + name.sin_addr.s_addr = htonl(INADDR_ANY); + } + else + { + name.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + + if(bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0) + { + perror("bind"); + close(sock); + return -1; + } + + if(listen(sock, 1) < 0) + { + perror("listen"); + close(sock); + return -1; + } + + return sock; +} + +int add_drive(int num, const char *dir) +{ + char path[PATH_MAX]; + DIR *pDir; + + if((num < 0) || (num >= MAX_HOSTDRIVES)) + { + printf("Invalid host driver number '%d'\n", num); + return 0; + } + + /* Make path */ + if(dir[0] != '/') + { + snprintf(path, PATH_MAX, "%s/%s", g_rootdir, dir); + } + else + { + strcpy(path, dir); + } + gen_path(path, 0); + + pDir = opendir(path); + if(pDir) + { + closedir(pDir); + if(pthread_mutex_lock(&g_drivemtx)) + { + printf("Couldn't lock mutex\n"); + return 0; + } + + strcpy(g_drives[num].rootdir, path); + strcpy(g_drives[num].currdir, "/"); + + pthread_mutex_unlock(&g_drivemtx); + } + else + { + printf("Invalid directory '%s'\n", path); + return 0; + } + + return 1; +} + +#define COMMAND_OK 0 +#define COMMAND_ERR 1 +#define COMMAND_HELP 2 + +struct ShellCmd +{ + const char *name; + const char *help; + int (*fn)(void); +}; + +int nocase_set(void) +{ + char *set; + + set = strtok(NULL, " \t"); + if(set) + { + if(strcmp(set, "on") == 0) + { + g_nocase = 1; + } + else if(strcmp(set, "off") == 0) + { + g_nocase = 0; + } + else + { + printf("Error setting nocase, invalid option '%s'\n", set); + } + } + else + { + printf("nocase: %s\n", g_nocase ? "on" : "off"); + } + + return COMMAND_OK; +} + +int msslash_set(void) +{ + char *set; + + set = strtok(NULL, " \t"); + if(set) + { + if(strcmp(set, "on") == 0) + { + g_msslash = 1; + } + else if(strcmp(set, "off") == 0) + { + g_msslash = 0; + } + else + { + printf("Error setting msslash, invalid option '%s'\n", set); + } + } + else + { + printf("msslash: %s\n", g_msslash ? "on" : "off"); + } + + return COMMAND_OK; +} + +int gdbdebug_set(void) +{ + char *set; + + set = strtok(NULL, " \t"); + if(set) + { + if(strcmp(set, "on") == 0) + { + g_gdbdebug = 1; + } + else if(strcmp(set, "off") == 0) + { + g_gdbdebug = 0; + } + else + { + printf("Error setting nocase, invalid option '%s'\n", set); + } + } + else + { + printf("gdbdebug: %s\n", g_gdbdebug ? "on" : "off"); + } + + return COMMAND_OK; +} + +int verbose_set(void) +{ + char *set; + + set = strtok(NULL, " \t"); + if(set) + { + g_verbose = atoi(set); + } + else + { + printf("verbose: %d\n", g_verbose); + } + + return COMMAND_OK; +} + +int list_drives(void) +{ + int i; + + for(i = 0; i < MAX_HOSTDRIVES; i++) + { + printf("host%d: %s\n", i, g_drives[i].rootdir); + } + + return COMMAND_OK; +} + +int mount_drive(void) +{ + char *num; + char *dir; + int val; + char *endp; + + num = strtok(NULL, " \t"); + dir = strtok(NULL, ""); + + if((!num) || (!dir)) + { + printf("Must specify a drive number and a directory\n"); + return COMMAND_ERR; + } + + val = strtoul(num, &endp, 10); + if(*endp) + { + printf("Invalid host driver number '%s'\n", num); + return COMMAND_ERR; + } + + if(!add_drive(val, dir)) + { + return COMMAND_ERR; + } + + return COMMAND_OK; +} + +void load_mapfile(const char *mapfile) +{ + char path[PATH_MAX]; + FILE *fp; + int line = 0; + + fp = fopen(mapfile, "r"); + if(fp == NULL) + { + printf("Couldn't open mapfile '%s'\n", g_mapfile); + return; + } + + while(fgets(path, PATH_MAX, fp)) + { + char *buf = path; + int len; + int num; + + line++; + /* Remove whitespace */ + len = strlen(buf); + while((len > 0) && (isspace(buf[len-1]))) + { + buf[len-1] = 0; + len--; + } + + while(isspace(*buf)) + { + buf++; + len--; + } + + if(!isdigit(*buf)) + { + printf("Line %d: Entry does not start with the host number\n", line); + continue; + } + + if(len > 0) + { + char *endp; + num = strtoul(buf, &endp, 10); + if((*endp != '=') || (*(endp+1) == 0) || (isspace(*(endp+1)))) + { + printf("Line %d: Entry is not of the form 'num=path'\n", line); + continue; + } + + endp++; + + add_drive(num, endp); + } + } + + fclose(fp); +} + +int load_drives(void) +{ + char *mapfile; + + mapfile = strtok(NULL, ""); + if(mapfile == NULL) + { + printf("Must specify a filename\n"); + return COMMAND_ERR; + } + + load_mapfile(mapfile); + + return COMMAND_OK; +} + +int save_drives(void) +{ + char *mapfile; + FILE *fp; + int i; + + mapfile = strtok(NULL, ""); + if(mapfile == NULL) + { + printf("Must specify a filename\n"); + return COMMAND_ERR; + } + + fp = fopen(mapfile, "w"); + if(fp == NULL) + { + printf("Couldn't open file '%s'\n", mapfile); + return COMMAND_ERR; + } + + for(i = 0; i < MAX_HOSTDRIVES; i++) + { + fprintf(fp, "%d=%s\n", i, g_drives[i].rootdir); + } + + fclose(fp); + + return COMMAND_OK; +} + +int print_wd(void) +{ + printf("%s\n", g_rootdir); + + return COMMAND_OK; +} + +int ch_dir(void) +{ + char *dir; + + dir = strtok(NULL, ""); + if(dir == NULL) + { + printf("Must specify a directory\n"); + return COMMAND_ERR; + } + + if(chdir(dir) == 0) + { + getcwd(g_rootdir, PATH_MAX); + } + else + { + printf("chdir: %s", strerror(errno)); + } + + return COMMAND_OK; +} + +int help_cmd(void) +{ + return COMMAND_HELP; +} + +struct ShellCmd g_commands[] = { + { "drives", "Print the current drives", list_drives }, + { "mount", "Mount a directory (mount num dir)", mount_drive }, + { "save", "Save the list of mounts to a file (save filename)", save_drives }, + { "load", "Load a list of mounts from a file (load filename)", load_drives }, + { "nocase", "Set case sensitivity (nocase on|off)", nocase_set }, + { "msslash", "Convert backslash to forward slash in filename", msslash_set }, + { "gdbdebug", "Set the GDB debug option (gdbdebug on|off)", gdbdebug_set }, + { "verbose", "Set the verbose level (verbose 0|1|2)", verbose_set }, + { "pwd", "Print the current directory", print_wd }, + { "cd", "Change the current local directory", ch_dir }, + { "help", "Print this help", help_cmd }, + { "exit", "Exit the application", exit_app }, +}; + +void parse_shell(char *buf) +{ + int len; + + if(buf == NULL) + { + exit_app(); + } + + if((buf) && (*buf)) + { +#ifdef READLINE_SHELL + add_history(buf); +#endif + /* Remove whitespace */ + len = strlen(buf); + while((len > 0) && (isspace(buf[len-1]))) + { + buf[len-1] = 0; + len--; + } + + while(isspace(*buf)) + { + buf++; + len--; + } + + if(len > 0) + { + if(*buf == '!') + { + system(buf+1); + } + else + { + const char *cmd; + int i; + int ret = COMMAND_HELP; + + cmd = strtok(buf, " \t"); + for(i = 0; i < (sizeof(g_commands) / sizeof(struct ShellCmd)); i++) + { + if(strcmp(cmd, g_commands[i].name) == 0) + { + if(g_commands[i].fn) + { + ret = g_commands[i].fn(); + } + break; + } + } + + if(ret == COMMAND_HELP) + { + int i; + + printf("-= Help =-\n"); + for(i = 0; i < (sizeof(g_commands) / sizeof(struct ShellCmd)); i++) + { + printf("%-10s: %s\n", g_commands[i].name, g_commands[i].help); + } + } + } + } + } +} + +#ifdef READLINE_SHELL +int init_readline(void) +{ + rl_callback_handler_install("sh> ", parse_shell); + + return 1; +} +#endif + +void *async_thread(void *arg) +{ + char buf[512]; + char *data; + struct AsyncCommand *cmd; + fd_set read_set, read_save; + struct sockaddr_in client; + socklen_t size; + int max_fd = 0; + int flag = 1; + int i; + + FD_ZERO(&read_save); + + if(!g_daemon) + { +#ifdef READLINE_SHELL + init_readline(); +#endif + + FD_SET(STDIN_FILENO, &read_save); + max_fd = STDIN_FILENO; + } + + for(i = 0; i < MAX_ASYNC_CHANNELS; i++) + { + if(g_servsocks[i] >= 0) + { + FD_SET(g_servsocks[i], &read_save); + if(g_servsocks[i] > max_fd) + { + max_fd = g_servsocks[i]; + } + } + } + + cmd = (struct AsyncCommand *) buf; + cmd->magic = LE32(ASYNC_MAGIC); + data = buf + sizeof(struct AsyncCommand); + + while(1) + { + read_set = read_save; + if(select(max_fd+1, &read_set, NULL, NULL, NULL) > 0) + { + if(!g_daemon) + { + if(FD_ISSET(STDIN_FILENO, &read_set)) + { +#ifdef READLINE_SHELL + rl_callback_read_char(); +#else + char buffer[4096]; + + if(fgets(buffer, sizeof(buffer), stdin)) + { + parse_shell(buffer); + } +#endif + } + } + + for(i = 0; i < MAX_ASYNC_CHANNELS; i++) + { + if(g_servsocks[i] >= 0) + { + if(FD_ISSET(g_servsocks[i], &read_set)) + { + if(g_clientsocks[i] >= 0) + { + FD_CLR(g_clientsocks[i], &read_save); + close(g_clientsocks[i]); + } + size = sizeof(client); + g_clientsocks[i] = accept(g_servsocks[i], (struct sockaddr *) &client, &size); + if(g_clientsocks[i] >= 0) + { + printf("Accepting async connection (%d) from %s\n", i, inet_ntoa(client.sin_addr)); + FD_SET(g_clientsocks[i], &read_save); + if((g_daemon) && (i == ASYNC_SHELL)) + { + /* Duplicate to stdout for the local shell */ + dup2(g_clientsocks[i], 1); + } + setsockopt(g_clientsocks[i], SOL_TCP, TCP_NODELAY, &flag, sizeof(int)); + if(g_clientsocks[i] > max_fd) + { + max_fd = g_clientsocks[i]; + } + } + } + } + } + + for(i = 0; i < MAX_ASYNC_CHANNELS; i++) + { + if(g_clientsocks[i] >= 0) + { + if(FD_ISSET(g_clientsocks[i], &read_set)) + { + int readbytes; + + readbytes = read(g_clientsocks[i], data, sizeof(buf) - sizeof(struct AsyncCommand)); + if(readbytes > 0) + { + if((i == ASYNC_GDB) && (g_gdbdebug)) + { + print_gdbdebug(1, (uint8_t *) data, readbytes); + } + + if(g_daemon) + { + if((i == ASYNC_SHELL) && (data[0] == '@')) + { + /* We assume locally it should be able to load everything in one go */ + if(readbytes < (sizeof(buf)-sizeof(struct AsyncCommand))) + { + data[readbytes] = 0; + } + else + { + data[sizeof(buf)-sizeof(struct AsyncCommand)-1] = 0; + } + + parse_shell(&data[1]); + continue; + } + } + + if(g_hDev) + { + cmd->channel = LE32(i); + euid_usb_bulk_write(g_hDev, 0x3, buf, readbytes+sizeof(struct AsyncCommand), 10000); + } + } + else + { + FD_CLR(g_clientsocks[i], &read_save); + if((g_daemon) && (i == ASYNC_SHELL)) + { + dup2(2, 1); + } + close(g_clientsocks[i]); + g_clientsocks[i] = -1; + printf("Closing async connection (%d)\n", i); + } + } + } + } + } + } + + return NULL; +} + +int main(int argc, char **argv) +{ + int i; + + printf("USBHostFS (c) TyRaNiD 2k6\n"); + printf("Built %s %s - $Revision: 2368 $\n", __DATE__, __TIME__); + + if(parse_args(argc, argv)) + { + pthread_t thid; + usb_init(); + + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + if(g_daemon) + { + pid_t pid = fork(); + + if(pid > 0) + { + /* Parent, just exit */ + return 0; + } + else if(pid < 0) + { + /* Error, print and exit */ + perror("fork:"); + return 1; + } + + /* Child, dup stdio to /dev/null and set process group */ + int fd = open("/dev/null", O_RDWR); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + setsid(); + } + + if(g_mapfile) + { + load_mapfile(g_mapfile); + } + + for(i = 0; i < MAX_ASYNC_CHANNELS; i++) + { + g_servsocks[i] = make_socket(g_baseport + i); + g_clientsocks[i] = -1; + } + + pthread_create(&thid, NULL, async_thread, NULL); + start_hostfs(); + } + else + { + print_help(); + } + + return 0; +} diff --git a/usbhostfs_pc/main.o b/usbhostfs_pc/main.o new file mode 100644 index 0000000..773a633 Binary files /dev/null and b/usbhostfs_pc/main.o differ diff --git a/usbhostfs_pc/mod.sh b/usbhostfs_pc/mod.sh new file mode 100755 index 0000000..849f09a --- /dev/null +++ b/usbhostfs_pc/mod.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# Quick simple tool to set SUID on the executable +sudo chown root:root usbhostfs_pc +sudo chmod +s usbhostfs_pc diff --git a/usbhostfs_pc/psp_fileio.h b/usbhostfs_pc/psp_fileio.h new file mode 100644 index 0000000..8a07364 --- /dev/null +++ b/usbhostfs_pc/psp_fileio.h @@ -0,0 +1,143 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * psp_fileio.h - Some definitions of PSP specific stuff for USB HostFS + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/usbhostfs_pc/psp_fileio.h $ + * $Id: psp_fileio.h 1816 2006-02-26 14:09:15Z tyranid $ + */ +#ifndef __PSP_FILEIO_H__ +#define __PSP_FILEIO_H__ + +#include + +#define PSP_O_RDONLY 0x0001 +#define PSP_O_WRONLY 0x0002 +#define PSP_O_RDWR (PSP_O_RDONLY | PSP_O_WRONLY) +#define PSP_O_NBLOCK 0x0004 +#define PSP_O_DIROPEN 0x0008 // Internal use for dopen +#define PSP_O_APPEND 0x0100 +#define PSP_O_CREAT 0x0200 +#define PSP_O_TRUNC 0x0400 +#define PSP_O_EXCL 0x0800 +#define PSP_O_NOWAIT 0x8000 + +#define PSP_SEEK_SET 0 +#define PSP_SEEK_CUR 1 +#define PSP_SEEK_END 2 + +/** Access modes for st_mode in SceIoStat (confirm?). */ +enum IOAccessModes +{ + /** Format bits mask */ + FIO_S_IFMT = 0xF000, + /** Symbolic link */ + FIO_S_IFLNK = 0x4000, + /** Directory */ + FIO_S_IFDIR = 0x1000, + /** Regular file */ + FIO_S_IFREG = 0x2000, + + /** Set UID */ + FIO_S_ISUID = 0x0800, + /** Set GID */ + FIO_S_ISGID = 0x0400, + /** Sticky */ + FIO_S_ISVTX = 0x0200, + + /** User access rights mask */ + FIO_S_IRWXU = 0x01C0, + /** Read user permission */ + FIO_S_IRUSR = 0x0100, + /** Write user permission */ + FIO_S_IWUSR = 0x0080, + /** Execute user permission */ + FIO_S_IXUSR = 0x0040, + + /** Group access rights mask */ + FIO_S_IRWXG = 0x0038, + /** Group read permission */ + FIO_S_IRGRP = 0x0020, + /** Group write permission */ + FIO_S_IWGRP = 0x0010, + /** Group execute permission */ + FIO_S_IXGRP = 0x0008, + + /** Others access rights mask */ + FIO_S_IRWXO = 0x0007, + /** Others read permission */ + FIO_S_IROTH = 0x0004, + /** Others write permission */ + FIO_S_IWOTH = 0x0002, + /** Others execute permission */ + FIO_S_IXOTH = 0x0001, +}; + +/** File modes, used for the st_attr parameter in SceIoStat (confirm?). */ +enum IOFileModes +{ + /** Format mask */ + FIO_SO_IFMT = 0x0038, // Format mask + /** Symlink */ + FIO_SO_IFLNK = 0x0008, // Symbolic link + /** Directory */ + FIO_SO_IFDIR = 0x0010, // Directory + /** Regular file */ + FIO_SO_IFREG = 0x0020, // Regular file + + /** Hidden read permission */ + FIO_SO_IROTH = 0x0004, // read + /** Hidden write permission */ + FIO_SO_IWOTH = 0x0002, // write + /** Hidden execute permission */ + FIO_SO_IXOTH = 0x0001, // execute +}; + +typedef struct ScePspDateTime { + uint16_t year; + uint16_t month; + uint16_t day; + uint16_t hour; + uint16_t minute; + uint16_t second; + uint32_t microsecond; +} ScePspDateTime; + +#define PSP_CHSTAT_MODE 0x01 +#define PSP_CHSTAT_ATTR 0x02 +#define PSP_CHSTAT_SIZE 0x04 +#define PSP_CHSTAT_CTIME 0x08 +#define PSP_CHSTAT_ATIME 0x10 +#define PSP_CHSTAT_MTIME 0x20 + +/** Structure to hold the status information about a file */ +typedef struct SceIoStat { + uint32_t mode; + uint32_t attr; + /** Size of the file in bytes. */ + int64_t size; + /** Creation time. */ + ScePspDateTime ctime; + /** Access time. */ + ScePspDateTime atime; + /** Modification time. */ + ScePspDateTime mtime; + /** Device-specific data. */ + uint32_t st_private[6]; +} SceIoStat; + +typedef struct SceIoDirent { + /** File status. */ + SceIoStat stat; + /** File name. */ + char name[256]; + /** Device-specific data. */ + uint32_t private; + uint32_t dummy; +} SceIoDirent; + +#endif diff --git a/usbhostfs_pc/usbhostfs_pc b/usbhostfs_pc/usbhostfs_pc new file mode 100755 index 0000000..47bba3c Binary files /dev/null and b/usbhostfs_pc/usbhostfs_pc differ diff --git a/usbshell/Makefile b/usbshell/Makefile new file mode 100644 index 0000000..6062397 --- /dev/null +++ b/usbshell/Makefile @@ -0,0 +1,21 @@ +TARGET = usbshell +OBJS = main.o exports.o + +# Use the kernel's small inbuilt libc +USE_KERNEL_LIBC = 1 +# Use only kernel libraries +USE_KERNEL_LIBS = 1 + +INCDIR = +#CFLAGS = -O2 -G0 -Wall -fno-builtin-printf -DDEBUG +CFLAGS = -Os -G0 -Wall -fno-builtin-printf -I../usbhostfs +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) +LDFLAGS = -L../libpsplink_driver -L../libusbhostfs_driver + +LIBDIR = + +LIBS = -lpsplink_driver -lusbhostfs_driver + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build_prx.mak diff --git a/usbshell/exports.exp b/usbshell/exports.exp new file mode 100644 index 0000000..b7940e8 --- /dev/null +++ b/usbshell/exports.exp @@ -0,0 +1,12 @@ +# Define the exports for the prx +PSP_BEGIN_EXPORTS + +# These four lines are mandatory (although you can add other functions like module_stop) +# syslib is a psynonym for the single mandatory export. +PSP_EXPORT_START(syslib, 0, 0x8000) +PSP_EXPORT_FUNC(module_start) +PSP_EXPORT_FUNC(module_stop) +PSP_EXPORT_VAR(module_info) +PSP_EXPORT_END + +PSP_END_EXPORTS diff --git a/usbshell/main.c b/usbshell/main.c new file mode 100644 index 0000000..c27c408 --- /dev/null +++ b/usbshell/main.c @@ -0,0 +1,112 @@ +/* + * PSPLINK + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPLINK root for details. + * + * main.c - PSPLINK USB Shell main code + * + * Copyright (c) 2006 James F + * + * $HeadURL: svn://svn.ps2dev.org/psp/trunk/psplinkusb/usbshell/main.c $ + * $Id: main.c 2034 2006-10-18 17:02:37Z tyranid $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +PSP_MODULE_INFO("USBShell", PSP_MODULE_KERNEL, 1, 1); + +#define MAX_CLI 4096 + +void ttySetUsbHandler(PspDebugPrintHandler usbShellHandler, PspDebugPrintHandler usbStdoutHandler, PspDebugPrintHandler usbStderrHandler); +int psplinkParseCommand(unsigned char *command); +void psplinkPrintPrompt(void); +void psplinkExitShell(void); + +struct AsyncEndpoint g_endp; + +int usbShellPrint(const char *data, int size) +{ + usbAsyncWrite(ASYNC_SHELL, data, size); + + return size; +} + +int usbStdoutPrint(const char *data, int size) +{ + usbAsyncWrite(ASYNC_STDOUT, data, size); + + return size; +} + +int usbStderrPrint(const char *data, int size) +{ + usbAsyncWrite(ASYNC_STDERR, data, size); + + return size; +} + +int main_thread(SceSize args, void *argp) +{ + unsigned char cli[MAX_CLI]; + int cli_pos = 0; + + usbAsyncRegister(ASYNC_SHELL, &g_endp); + ttySetUsbHandler(usbShellPrint, usbStdoutPrint, usbStderrPrint); + usbWaitForConnect(); + psplinkPrintPrompt(); + + while(1) + { + if(usbAsyncRead(ASYNC_SHELL, &cli[cli_pos], 1) < 1) + { + sceKernelDelayThread(250000); + continue; + } + + if(cli[cli_pos] == '\n') + { + cli[cli_pos] = 0; + if(psplinkParseCommand(cli) == 1) + { + psplinkExitShell(); + } + cli_pos = 0; + } + else + { + if(cli_pos < (MAX_CLI-1)) + { + cli_pos++; + } + } + } + + return 0; +} + +/* Entry point */ +int module_start(SceSize args, void *argp) +{ + int thid; + + /* Create a high priority thread */ + thid = sceKernelCreateThread("USBShell", main_thread, 12, 0x2000, 0, NULL); + if(thid >= 0) + { + sceKernelStartThread(thid, args, argp); + } + return 0; +} + +/* Module stop entry */ +int module_stop(SceSize args, void *argp) +{ + return 0; +} diff --git a/usbshell/psplink.ini b/usbshell/psplink.ini new file mode 100644 index 0000000..2a43377 --- /dev/null +++ b/usbshell/psplink.ini @@ -0,0 +1,48 @@ +# Example psplink configuration file. + +# usbmass=[0 1] Enable USB mass storage. Set to 1 to enable automatically +usbmass=0 + +# usbhost=[0 1] Enable USB host file system. Set to 1 to enable automatically +usbhost=1 + +# pluser=[0 1] Enable the PSPLink user module +pluser=1 + +# resetonexit=[0 1] Specify wheher to reset psplink when sceKernelExitGame +# is called +resetonexit=1 + +# sioshell=[0 1] Specify whether to start up the sio shell +sioshell=1 + +# wifi=[0..N] Specify wifi should be enabled, the number is the +# configuration to use if > 0 +wifi=0 + +# wifishell=[0 1] Specify whether to start up the wifi shell +wifishell=0 + +# prompt=... Set the psplink shell prompt +# There are some escape characters, mainly %d to print the current dir +prompt="%d> " + +# path=... Set the psplink shell path +# Each path is separated by a semi-colon, you can specify up to around 128 characters +# path=ms0:/apps;ms0:/ + +# pcterm=[0 1] Indicates whether we are using pcterm as a client or normal +# tools +pcterm=1 + +# baud=[4800..115200] Set the SIO baud rate (should only use as a last resort) +# baud=115200 + +# modload=path Load a module on start up, repeat as necessary for more modules + +# Example: load the modules for networking +# modload=flash0:/kd/ifhandle.prx +# modload=flash0:/kd/pspnet.prx +# modload=flash0:/kd/pspnet_inet.prx +# modload=flash0:/kd/pspnet_apctl.prx +# modload=flash0:/kd/pspnet_resolver.prx diff --git a/windows/COPYING_GPL.txt b/windows/COPYING_GPL.txt new file mode 100644 index 0000000..45645b4 --- /dev/null +++ b/windows/COPYING_GPL.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/windows/COPYING_LGPL.txt b/windows/COPYING_LGPL.txt new file mode 100644 index 0000000..6d6873c --- /dev/null +++ b/windows/COPYING_LGPL.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/windows/README.txt b/windows/README.txt new file mode 100644 index 0000000..7af873e --- /dev/null +++ b/windows/README.txt @@ -0,0 +1,16 @@ + +This is libusb-win32 (http://libusb-win32.sourceforge.net) version 20051120. +Libusb-win32 is a library that allows userspace application to access USB +devices on Windows operation systems (Win98SE, WinME, Win2k, WinXP). +It is derived from and fully API compatible to libusb available at +http://libusb.sourceforge.net. + +For more information visit the project's web site at: + +http://libusb-win32.sourceforge.net +http://sourceforge.net/projects/libusb-win32 + +* PSPLINK NOTE * + +This is just the basics from the libusb snapshot to get psplink working on a Windows platform. + diff --git a/windows/driver/libusb0.dll b/windows/driver/libusb0.dll new file mode 100755 index 0000000..d8f9502 Binary files /dev/null and b/windows/driver/libusb0.dll differ diff --git a/windows/driver/libusb0.sys b/windows/driver/libusb0.sys new file mode 100755 index 0000000..715ee26 Binary files /dev/null and b/windows/driver/libusb0.sys differ diff --git a/windows/driver/psp.cat b/windows/driver/psp.cat new file mode 100644 index 0000000..2ef39c5 --- /dev/null +++ b/windows/driver/psp.cat @@ -0,0 +1,3 @@ +This file will contain the digital signature of the files to be installed +on the system. +This file will be provided by Microsoft upon certification of your drivers. diff --git a/windows/driver/psp.inf b/windows/driver/psp.inf new file mode 100644 index 0000000..78e8e58 --- /dev/null +++ b/windows/driver/psp.inf @@ -0,0 +1,102 @@ +[Version] +Signature = "$Chicago$" +provider = %manufacturer% +DriverVer = 11/20/2005,20051120 +CatalogFile = psp.cat + +Class = LibUsbDevices +ClassGUID = {EB781AAF-9C70-4523-A5DF-642A87ECA567} + +[ClassInstall] +AddReg=ClassInstall.AddReg + +[ClassInstall32] +AddReg=ClassInstall.AddReg + +[ClassInstall.AddReg] +HKR,,,,"LibUSB-Win32 Devices" +HKR,,Icon,,"-20" + +[Manufacturer] +%manufacturer%=Devices + +;-------------------------------------------------------------------------- +; Files +;-------------------------------------------------------------------------- + +[SourceDisksNames] +1 = "Libusb-Win32 Driver Installation Disk",, + +[SourceDisksFiles] +libusb0.sys = 1,, +libusb0.dll = 1,, + +[DestinationDirs] +LIBUSB.Files.Sys = 10,System32\Drivers +LIBUSB.Files.Dll = 10,System32 + +[LIBUSB.Files.Sys] +libusb0.sys + +[LIBUSB.Files.Dll] +libusb0.dll + +;-------------------------------------------------------------------------- +; Device driver +;-------------------------------------------------------------------------- + +[LIBUSB_DEV] +CopyFiles = LIBUSB.Files.Sys, LIBUSB.Files.Dll +AddReg = LIBUSB_DEV.AddReg + +[LIBUSB_DEV.NT] +CopyFiles = LIBUSB.Files.Sys, LIBUSB.Files.Dll + +[LIBUSB_DEV.HW] +DelReg = LIBUSB_DEV.DelReg.HW +AddReg = LIBUSB_DEV.AddReg.HW + +[LIBUSB_DEV.NT.HW] +DelReg = LIBUSB_DEV.DelReg.HW +AddReg = LIBUSB_DEV.AddReg.HW + +[LIBUSB_DEV.NT.Services] +AddService = libusb0, 0x00000002, LIBUSB.AddService + +[LIBUSB_DEV.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,libusb0.sys + +[LIBUSB_DEV.DelReg.HW] +HKR,,LowerFilters +HKR,,UpperFilters + +[LIBUSB_DEV.AddReg.HW] +HKR,,libusb_is_device_driver, 0x00010001 ,1 + +;-------------------------------------------------------------------------- +; Services +;-------------------------------------------------------------------------- + +[LIBUSB.AddService] +DisplayName = "LibUsb-Win32 - Kernel Driver 11/20/2005, 20051120" +ServiceType = 1 +StartType = 3 +ErrorControl = 0 +ServiceBinary = %12%\libusb0.sys + +;-------------------------------------------------------------------------- +; Devices +;-------------------------------------------------------------------------- + +[Devices] +"PSP Type B"=LIBUSB_DEV, USB\VID_054c&PID_01c9 + + +;-------------------------------------------------------------------------- +; Strings +;-------------------------------------------------------------------------- + +[Strings] + +manufacturer = "Sony" diff --git a/windows/driver_x64/libusb0.dll b/windows/driver_x64/libusb0.dll new file mode 100644 index 0000000..316459a Binary files /dev/null and b/windows/driver_x64/libusb0.dll differ diff --git a/windows/driver_x64/libusb0.sys b/windows/driver_x64/libusb0.sys new file mode 100644 index 0000000..9e44a3b Binary files /dev/null and b/windows/driver_x64/libusb0.sys differ diff --git a/windows/driver_x64/libusb0_x64.dll b/windows/driver_x64/libusb0_x64.dll new file mode 100644 index 0000000..aef485d Binary files /dev/null and b/windows/driver_x64/libusb0_x64.dll differ diff --git a/windows/driver_x64/libusb0_x64.sys b/windows/driver_x64/libusb0_x64.sys new file mode 100644 index 0000000..b7f0f29 Binary files /dev/null and b/windows/driver_x64/libusb0_x64.sys differ diff --git a/windows/driver_x64/psp.cat b/windows/driver_x64/psp.cat new file mode 100644 index 0000000..2ef39c5 --- /dev/null +++ b/windows/driver_x64/psp.cat @@ -0,0 +1,3 @@ +This file will contain the digital signature of the files to be installed +on the system. +This file will be provided by Microsoft upon certification of your drivers. diff --git a/windows/driver_x64/psp.inf b/windows/driver_x64/psp.inf new file mode 100644 index 0000000..33f9c32 --- /dev/null +++ b/windows/driver_x64/psp.inf @@ -0,0 +1,136 @@ +[Version] +Signature = "$Chicago$" +provider = %manufacturer% +DriverVer = 08/27/2006,0.1.12.0 +CatalogFile = psp.cat +CatalogFile.NT = psp.cat +CatalogFile.NTAMD64 = psp_x64.cat + +Class = LibUsbDevices +ClassGUID = {EB781AAF-9C70-4523-A5DF-642A87ECA567} + +[ClassInstall] +AddReg=libusb_class_install_add_reg + +[ClassInstall32] +AddReg=libusb_class_install_add_reg + +[libusb_class_install_add_reg] +HKR,,,,"LibUSB-Win32 Devices" +HKR,,Icon,,"-20" + +[Manufacturer] +%manufacturer%=Devices,NT,NTAMD64 + +;-------------------------------------------------------------------------- +; Files +;-------------------------------------------------------------------------- + +[SourceDisksNames] +1 = "Libusb-Win32 Driver Installation Disk",, + +[SourceDisksFiles] +libusb0.sys = 1,, +libusb0.dll = 1,, +libusb0_x64.sys = 1,, +libusb0_x64.dll = 1,, + +[DestinationDirs] +libusb_files_sys = 10,system32\drivers +libusb_files_sys_x64 = 10,system32\drivers +libusb_files_dll = 10,system32 +libusb_files_dll_wow64 = 10,syswow64 +libusb_files_dll_x64 = 10,system32 + +[libusb_files_sys] +libusb0.sys + +[libusb_files_sys_x64] +libusb0.sys,libusb0_x64.sys + +[libusb_files_dll] +libusb0.dll + +[libusb_files_dll_wow64] +libusb0.dll + +[libusb_files_dll_x64] +libusb0.dll,libusb0_x64.dll + +;-------------------------------------------------------------------------- +; Device driver +;-------------------------------------------------------------------------- + +[LIBUSB_DEV] +CopyFiles = libusb_files_sys, libusb_files_dll +AddReg = libusb_add_reg + +[LIBUSB_DEV.NT] +CopyFiles = libusb_files_sys, libusb_files_dll + +[LIBUSB_DEV.NTAMD64] +CopyFiles = libusb_files_sys_x64, libusb_files_dll_wow64, libusb_files_dll_x64 + +[LIBUSB_DEV.HW] +DelReg = libusb_del_reg_hw +AddReg = libusb_add_reg_hw + +[LIBUSB_DEV.NT.HW] +DelReg = libusb_del_reg_hw +AddReg = libusb_add_reg_hw + +[LIBUSB_DEV.NTAMD64.HW] +DelReg = libusb_del_reg_hw +AddReg = libusb_add_reg_hw + +[LIBUSB_DEV.NT.Services] +AddService = libusb0, 0x00000002, libusb_add_service + +[LIBUSB_DEV.NTAMD64.Services] +AddService = libusb0, 0x00000002, libusb_add_service + +[libusb_add_reg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,libusb0.sys + +; Older versions of this .inf file installed filter drivers. They are not +; needed any more and must be removed +[libusb_del_reg_hw] +HKR,,LowerFilters +HKR,,UpperFilters + +; Device properties +[libusb_add_reg_hw] +HKR,,SurpriseRemovalOK, 0x00010001, 1 + +;-------------------------------------------------------------------------- +; Services +;-------------------------------------------------------------------------- + +[libusb_add_service] +DisplayName = "LibUsb-Win32 - Kernel Driver 08/27/2006, 0.1.12.0" +ServiceType = 1 +StartType = 3 +ErrorControl = 0 +ServiceBinary = %12%\libusb0.sys + +;-------------------------------------------------------------------------- +; Devices +;-------------------------------------------------------------------------- + +[Devices] +"PSP"=LIBUSB_DEV, USB\VID_054c&PID_01c9 + +[Devices.NT] +"PSP"=LIBUSB_DEV, USB\VID_054c&PID_01c9 + +[Devices.NTAMD64] +"PSP"=LIBUSB_DEV, USB\VID_054c&PID_01c9 + + +;-------------------------------------------------------------------------- +; Strings +;-------------------------------------------------------------------------- + +[Strings] +manufacturer = "Sony" diff --git a/windows/driver_x64/psp_x64.cat b/windows/driver_x64/psp_x64.cat new file mode 100644 index 0000000..2ef39c5 --- /dev/null +++ b/windows/driver_x64/psp_x64.cat @@ -0,0 +1,3 @@ +This file will contain the digital signature of the files to be installed +on the system. +This file will be provided by Microsoft upon certification of your drivers. diff --git a/windows/lib/libusb.a b/windows/lib/libusb.a new file mode 100644 index 0000000..566b88d Binary files /dev/null and b/windows/lib/libusb.a differ diff --git a/windows/lib/usb.h b/windows/lib/usb.h new file mode 100644 index 0000000..8573a00 --- /dev/null +++ b/windows/lib/usb.h @@ -0,0 +1,391 @@ +#ifndef __USB_H__ +#define __USB_H__ + +#include +#include + +/* + * 'interface' is defined somewhere in the Windows header files. This macro + * is deleted here to avoid conflicts and compile errors. + */ + +#ifdef interface +#undef interface +#endif + +/* + * PATH_MAX from limits.h can't be used on Windows if the dll and + * import libraries are build/used by different compilers + */ + +#define LIBUSB_PATH_MAX 512 + + +/* + * USB spec information + * + * This is all stuff grabbed from various USB specs and is pretty much + * not subject to change + */ + +/* + * Device and/or Interface Class codes + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_VENDOR_SPEC 0xff + +/* + * Descriptor types + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID 0x21 +#define USB_DT_REPORT 0x22 +#define USB_DT_PHYSICAL 0x23 +#define USB_DT_HUB 0x29 + +/* + * Descriptor sizes per descriptor type + */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 + + +/* ensure byte-packed structures */ +#include + + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + unsigned char bLength; + unsigned char bDescriptorType; +}; + +/* String descriptor */ +struct usb_string_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wData[1]; +}; + +/* HID descriptor */ +struct usb_hid_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdHID; + unsigned char bCountryCode; + unsigned char bNumDescriptors; +}; + +/* Endpoint descriptor */ +#define USB_MAXENDPOINTS 32 +struct usb_endpoint_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bEndpointAddress; + unsigned char bmAttributes; + unsigned short wMaxPacketSize; + unsigned char bInterval; + unsigned char bRefresh; + unsigned char bSynchAddress; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + +#define USB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_TYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_TYPE_CONTROL 0 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1 +#define USB_ENDPOINT_TYPE_BULK 2 +#define USB_ENDPOINT_TYPE_INTERRUPT 3 + +/* Interface descriptor */ +#define USB_MAXINTERFACES 32 +struct usb_interface_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; + + struct usb_endpoint_descriptor *endpoint; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + +#define USB_MAXALTSETTING 128 /* Hard limit */ + +struct usb_interface { + struct usb_interface_descriptor *altsetting; + + int num_altsetting; +}; + +/* Configuration descriptor information.. */ +#define USB_MAXCONFIG 8 +struct usb_config_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char MaxPower; + + struct usb_interface *interface; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +}; + +/* Device descriptor */ +struct usb_device_descriptor { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdUSB; + unsigned char bDeviceClass; + unsigned char bDeviceSubClass; + unsigned char bDeviceProtocol; + unsigned char bMaxPacketSize0; + unsigned short idVendor; + unsigned short idProduct; + unsigned short bcdDevice; + unsigned char iManufacturer; + unsigned char iProduct; + unsigned char iSerialNumber; + unsigned char bNumConfigurations; +}; + +struct usb_ctrl_setup { + unsigned char bRequestType; + unsigned char bRequest; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; +}; + +/* + * Standard requests + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +/* 0x02 is reserved */ +#define USB_REQ_SET_FEATURE 0x03 +/* 0x04 is reserved */ +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* + * Various libusb API related stuff + */ + +#define USB_ENDPOINT_IN 0x80 +#define USB_ENDPOINT_OUT 0x00 + +/* Error codes */ +#define USB_ERROR_BEGIN 500000 + +/* + * This is supposed to look weird. This file is generated from autoconf + * and I didn't want to make this too complicated. + */ +#define USB_LE16_TO_CPU(x) + +/* Data types */ +/* struct usb_device; */ +/* struct usb_bus; */ + +struct usb_device { + struct usb_device *next, *prev; + + char filename[LIBUSB_PATH_MAX]; + + struct usb_bus *bus; + + struct usb_device_descriptor descriptor; + struct usb_config_descriptor *config; + + void *dev; /* Darwin support */ + + unsigned char devnum; + + unsigned char num_children; + struct usb_device **children; +}; + +struct usb_bus { + struct usb_bus *next, *prev; + + char dirname[LIBUSB_PATH_MAX]; + + struct usb_device *devices; + unsigned long location; + + struct usb_device *root_dev; +}; + +/* Version information, Windows specific */ +struct usb_version { + struct { + int major; + int minor; + int micro; + int nano; + } dll; + struct { + int major; + int minor; + int micro; + int nano; + } driver; +}; + + +struct usb_dev_handle; +typedef struct usb_dev_handle usb_dev_handle; + +/* Variables */ +#ifndef __USB_C__ +#define usb_busses usb_get_busses() +#endif + + + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + /* Function prototypes */ + + /* usb.c */ + usb_dev_handle *usb_open(struct usb_device *dev); + int usb_close(usb_dev_handle *dev); + int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, + size_t buflen); + int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, + size_t buflen); + + /* descriptors.c */ + int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep, + unsigned char type, unsigned char index, + void *buf, int size); + int usb_get_descriptor(usb_dev_handle *udev, unsigned char type, + unsigned char index, void *buf, int size); + + /* .c */ + int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); + int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); + int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); + int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, + int timeout); + int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, + int value, int index, char *bytes, int size, + int timeout); + int usb_set_configuration(usb_dev_handle *dev, int configuration); + int usb_claim_interface(usb_dev_handle *dev, int interface); + int usb_release_interface(usb_dev_handle *dev, int interface); + int usb_set_altinterface(usb_dev_handle *dev, int alternate); + int usb_resetep(usb_dev_handle *dev, unsigned int ep); + int usb_clear_halt(usb_dev_handle *dev, unsigned int ep); + int usb_reset(usb_dev_handle *dev); + + char *usb_strerror(void); + + void usb_init(void); + void usb_set_debug(int level); + int usb_find_busses(void); + int usb_find_devices(void); + struct usb_device *usb_device(usb_dev_handle *dev); + struct usb_bus *usb_get_busses(void); + + + /* Windows specific functions */ + + #define LIBUSB_HAS_INSTALL_SERVICE_NP 1 + int usb_install_service_np(void); + void CALLBACK usb_install_service_np_rundll(HWND wnd, HINSTANCE instance, + LPSTR cmd_line, int cmd_show); + + #define LIBUSB_HAS_UNINSTALL_SERVICE_NP 1 + int usb_uninstall_service_np(void); + void CALLBACK usb_uninstall_service_np_rundll(HWND wnd, HINSTANCE instance, + LPSTR cmd_line, int cmd_show); + + #define LIBUSB_HAS_INSTALL_DRIVER_NP 1 + int usb_install_driver_np(const char *inf_file); + void CALLBACK usb_install_driver_np_rundll(HWND wnd, HINSTANCE instance, + LPSTR cmd_line, int cmd_show); + + #define LIBUSB_HAS_TOUCH_INF_FILE_NP 1 + int usb_touch_inf_file_np(const char *inf_file); + void CALLBACK usb_touch_inf_file_np_rundll(HWND wnd, HINSTANCE instance, + LPSTR cmd_line, int cmd_show); + + const struct usb_version *usb_get_version(void); + + int usb_isochronous_setup_async(usb_dev_handle *dev, void **context, + unsigned char ep, int pktsize); + int usb_bulk_setup_async(usb_dev_handle *dev, void **context, + unsigned char ep); + int usb_interrupt_setup_async(usb_dev_handle *dev, void **context, + unsigned char ep); + + int usb_submit_async(void *context, char *bytes, int size); + int usb_reap_async(void *context, int timeout); + int usb_reap_async_nocancel(void *context, int timeout); + int usb_cancel_async(void *context); + int usb_free_async(void **context); + + +#ifdef __cplusplus +} +#endif + +#endif /* __USB_H__ */ +