/* * Action Replay File Server Release 2 * File i/o and support library routines for * Codewarrior to access the ARS fileserver. * * Original R3000 assembler code for Action Replay comms channel * based on DEMO.MIP by Snake & McBain of EZ-o-Ray. * * CodeWarrior library wrapper and file server support: * Craig at Data Uncertain * * New In This Release: * o Better retry / no-locking * o Kernal jump table patching to catch all printf'ed output including * messages from the Playstation OS (eg. "ResetGraph:jtb=...") * o Support for GNU C */ #include #include #include #include #include #include #include #include // Maximum number of retries on an open operation #define MAX_RETRY 4 /* *=========================================================== * ACTION REPLAY COMMS CHANNEL LOW-LEVEL SUPPORT FUNCTIONS *=========================================================== */ #ifdef __MWERKS__ asm unsigned char ar_rw(unsigned char v); asm void ar_w(unsigned char v); asm unsigned char ar_r(void); #else unsigned char ar_rw(unsigned char v); void ar_w(unsigned char v); unsigned char ar_r(void); #endif void cg_print(char *t); int cg_printf(char *fmt, ...); long cg_bload(char *fname, unsigned char *buf); long cg_fopen(char *fname, int mode); long cg_fread(long handle, char *bufb, long rnum); void cg_fclose(long handle); int cg_fwrite0(long fd, char *buf,int nbytes); int outchar(char c); unsigned long patchA0(int op_code, void *fptr); unsigned long patchB0(int op_code, void *fptr); long ar_rw_error_status=0; long ar_time_out=0x1d09000; /* * Recieve and Send to/from Action Replay comms link. * - we use this function most of the time as it provides * a nice handshake over the link. */ #ifdef __MWERKS__ /* * Metrowerks CodeWarrior inline assembler stuff */ asm unsigned char ar_rw(unsigned char v) { lui v1,0x1F02 lui a1,0x1F06 lw t0,ar_time_out @check: lbu v0,0x0010(v1) // read ar statusport nop andi v0,v0,0x0001 subu t0,t0,1 nop beq t0,zero,@abort // count down and abort if we need to nop beq v0,zero,@check // loop till bit 0 is 1 nop lbu v0,0x0000(a1) // read data sb a0,0x0008(a1) // write data jr ra nop @abort: // Aborting, report a possible comms error li t0,1 sw t0,ar_rw_error_status lbu v0,0x0000(a1) // read data sb a0,0x0008(a1) // write data jr ra nop } /* * Send a character to the Action Replay comms channel * - note that this is NOT handshaked. */ asm void ar_w(unsigned char v) { lui a1,0x1F06 sb a0,0x0008(a1) // write data jr ra nop } /* * Wait for a character to be delivered from the PC, * then read it. * - note that there is no way for the PC to know that * the character has been read using this function. */ asm unsigned char ar_r(void) { lui v1,0x1F02 lui a1,0x1F06 @check: lbu v0,0x0010(v1) ; read ar statusport nop andi v0,v0,0x0001 beq v0,zero,@check ; loop till bit 0 is 1 nop lbu v0,0x0000(a1) ; read data jr ra nop } #else /* * GNU C's weird and wonderful inline assembler stuff */ unsigned char ar_r(void) { asm(" sub $9,$9,$9;"); asm(" li $3,0x1F020000; \ li $5,0x1F060000; \ check_r:; \ lbu $2,0x0010($3); \ nop; \ andi $2,$2,0x0001; \ beq $2,$9,check_r; \ nop; \ lbu $2,0x0000($5);"); } void ar_w(unsigned char v) { asm(" li $5,0x1F060000; \ sb $4,0x0008($5); \ jr $31; \ nop;"); } unsigned char ar_rw(unsigned char v) { asm(" li $3,0x1F020000;"); asm(" li $5,0x1F060000;"); asm(" lw $8,ar_time_out;"); asm(" sub $9,$9,$9;"); asm("check_rw:; \ lbu $2,0x0010($3); \ nop ; \ andi $2,$2,0x0001; \ sub $8,$8,1; \ nop; \ beq $8,$9,abort_rw; \ nop; \ beq $2,$9,check_rw; \ nop; \ j done_rw; \ abort_rw:; \ li $8,1; \ sw $8,ar_rw_error_status; \ done_rw:; \ lbu $2,0x0000($5); \ sb $4,0x0008($5);"); } #endif /* *=================================================== * Playstation OS kernal jump table patching support *=================================================== * Found these by fishing with the Action Replay's * standard PC software.... *=================================================== */ // Address' of the A0 & B0 jump tables unsigned long *A0_jump_table=(unsigned long*)0x00000200L; unsigned long *B0_jump_table=(unsigned long*)0x00000874L; /* * Patch a vector in the $a0 table, returning the old vector */ unsigned long patchA0(int op_code, void *fptr) { unsigned long old; old=A0_jump_table[op_code]; A0_jump_table[op_code]=(unsigned long)fptr; return old; } /* * Patch a vector in the $b0 table, returning the old vector */ unsigned long patchB0(int op_code, void *fptr) { unsigned long old; old=B0_jump_table[op_code]; B0_jump_table[op_code]=(unsigned long)fptr; return old; } /* *=========================================================== * ARS File Server Library functions *=========================================================== * The following functions implement a higher level comms * protocol to talk to the PC based file server program ARS *=========================================================== */ void *kernal_vectors[14]; /* * Initialise the Action Replay comms link. * - We patch into the Playstation OS kernal jump table here * to redirect standard file access & printing to the ARS console. * - We also send a connect message to the ARS console.... */ void cg_fsinit(void) { // Patch in redirected puts() and printf() kernal_vectors[0]=(void*)patchA0(0x3f,cg_printf); kernal_vectors[1]=(void*)patchA0(0x3e,cg_print); kernal_vectors[2]=(void*)patchB0(0x3f,cg_print); // Patch in redirected file open kernal_vectors[3]=(void*)patchA0(0,cg_fopen); kernal_vectors[4]=(void*)patchB0(0x32,cg_fopen); // Patch in redirected file read kernal_vectors[5]=(void*)patchA0(2,cg_fread); kernal_vectors[6]=(void*)patchB0(0x34,cg_fread); // Patch in redirected file close kernal_vectors[7]=(void*)patchA0(4,cg_fclose); kernal_vectors[8]=(void*)patchB0(0x36,cg_fclose); // Patch in a bit of write, just enough to put stdout onto the ARS console kernal_vectors[9]=(void*)patchA0(3,cg_fwrite0); kernal_vectors[10]=(void*)patchB0(0x35,cg_fwrite0); // Patch putc() and putchar() kernal_vectors[11]=(void*)patchA0(0x3c,outchar); kernal_vectors[12]=(void*)patchB0(0x3b,outchar); kernal_vectors[13]=(void*)patchB0(0x3d,outchar); // Send the connect/ready message to the ARS server on the PC ar_rw('R'); } /* * Unhook the kernal to allow re-start without reset */ void cg_shutdown(void) { patchA0(0x3f,kernal_vectors[0]); patchA0(0x3e,kernal_vectors[1]); patchB0(0x3f,kernal_vectors[2]); patchA0(0,kernal_vectors[3]); patchB0(0x32,kernal_vectors[4]); patchA0(2,kernal_vectors[5]); patchB0(0x34,kernal_vectors[6]); patchA0(4,kernal_vectors[7]); patchB0(0x36,kernal_vectors[8]); patchA0(3,kernal_vectors[9]); patchB0(0x35,kernal_vectors[10]); patchA0(0x3c,kernal_vectors[11]); patchB0(0x3b,kernal_vectors[12]); patchB0(0x3d,kernal_vectors[13]); ar_rw('R'); } /* * Kernal patch function to catch writes to handle's 0 & 2 * (stdout/stderr) and redirect them to the ARS console. */ int cg_fwrite0(long fd, char *buf,int nbytes) { char ack; // Request a print do{ ack=ar_rw('P'); }while(ack!='p'); // Send string (0 terminated) while(nbytes--) { ar_rw(*buf); buf++; } ar_rw(0); } /* * Print a C-String ('\0' terminated) via the AR-Comms channel, * using the ARS protocol. */ void cg_print(char *t) { char ack; // Request a print do{ ack=ar_rw('P'); }while(ack!='p'); // Send string (0 terminated) while(*t) { ar_rw(*t); t++; } ar_rw(0); } /* * Kernal patch kludge function for putchar() */ int outchar(char c) { char buf[2]; buf[0]=c; buf[1]='\0'; cg_print(buf); return 0; } /* * Standard printf implementation via the AR-Comms channel, * using the ARS protocol. */ int cg_printf(char *fmt, ...) { va_list ap; char *p, *sval, *t; char line_buffer[200]; long lval; t=line_buffer; va_start(ap,fmt); for(p=fmt; *p; p++) { if(*p!='%') { *t++=*p; }else{ if(p[1]=='l') p++; switch(*++p) { case 'd': lval=va_arg(ap,long); sprintf(t,"%d",lval); while(*t) t++; break; case 'x': lval=va_arg(ap,long); sprintf(t,"%x",lval); while(*t) t++; break; case 's': for(sval=va_arg(ap, char*); *sval; *t++=*sval++); break; case 'c': *t++=(char)va_arg(ap,long); break; default: *t++=*p; break; } } } *t='\0'; va_end(ap); cg_print(line_buffer); return 0; } /* * Load an entire file into memory at buf * - equivalent to MWbload from the Metrowerks MWDebugIO lib. */ long cg_bload(char *fname, unsigned char *buf) { char ack,*t; int flen,f; // Request a load do{ ack=ar_rw('L'); }while(ack!='l'); // Send filename t=fname; while(*t) { ar_rw(*t); t++; } ar_rw(0xff); ack=ar_rw(0); if(ack=='n') { return -1; } f=ar_rw(0); flen=f<<24; f=ar_rw(0); flen|=f<<16; f=ar_rw(0); flen|=f<<8; f=ar_rw(0); flen|=f; f=flen; while(f--) { *buf++=ar_rw('D'); } return flen; } /* * Open a file on the PC using ARS. */ long cg_fopen(char *fname, int mode) { char ack=-1,*t; short f; if(strncmp("cdrom:",fname,6)==0) fname+=6; for(f=0; (f>24)&0xff; ar_rw(d); d=(num>>16)&0xff; ar_rw(d); d=(num>>8)&0xff; ar_rw(d); d=num&0xff; ar_rw(d); // Get length actually read f=ar_rw(0); rlen=f<<24; f=ar_rw(0); rlen|=f<<16; f=ar_rw(0); rlen|=f<<8; f=ar_rw(0); rlen|=f; } ar_time_out=0x10000; if(rlen>0) { // Get data num=rlen; while((num--)&&(ar_rw_error_status==0)) { *buf++=ar_rw(0xff); } } ExitCriticalSection(); ar_time_out=0x1d09000; // Error recovery on read.... if (ar_rw_error_status) { ar_rw_error_status=0; // Request a re-read /* do{ ack=ar_rw('X'); }while(ack!='x'); d=(char)(handle&0xff); ar_rw(d);*/ ar_rw(0); goto read_error_recovery; } return rlen; }