/*============================================================================== * patch.c: Apply the patches to a program. * * $Id: patch.c,v 1.8 2004/07/15 13:15:25 Olivier Exp $ * * - GhostBuster for TI89 Titanium - * Copyright (C) 2004 Olivier Armand * and Kevin Kofler * * 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. * * In addition, as a special exception, Olivier Armand and Kevin Kofler give * permission to link the code of this program with the ttunpack decompression * library by the TI-Chess Team, and distribute linked combinations including * the two. You must obey the GNU General Public License in all respects for * all of the code used other than the ttunpack decompression library. If you * modify this program, you may extend this exception to your version of the * program, but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. * *==============================================================================*/ #include "common.h" #include "patch.h" /* Apply all the patches needed to the program pointed to by 'prgm', of length * 'prgm_len'. Return the number of patches applied. */ unsigned short ApplyPatches(unsigned char *prgm, unsigned long prgm_len) { short opcode; unsigned short patches_num; unsigned char *prgm_head, *prgm_tail; prgm_tail = prgm + prgm_len; for (patches_num = 0, prgm_head = prgm; prgm < prgm_tail;) { /****************************************************************************** * Change the accesses to the ghost space of the RAM (0x40000 + n), that does * not exist anymore on TI89 Titanium, to its new ghost space (0x200000 + n). * This patch series is only for interrupt redirection. Instructions used for * bypassing the execution protection use the other patch series. * * The instructions seeked and patched are: * - move.l ?,$400xx with xx < 0xC0 (that modifies the vectors table) * - move.l $400xx,? * - move.l #$400xx,an (that could be followed by move.l ?,-(an)/(an)/(an)+ ) * - lea.l $400xx,an (that could be followed by move.l ?,-(an)/(an)/(an)+ ) *****************************************************************************/ if (/* Is it an access to the vectors table from the ghost space */ (*(unsigned long*)prgm & 0xFFFFFF00) == 0x40000 /* Valid vectors are below 0xC0 in the table */ && (unsigned char)(*(unsigned long*)prgm) < 0xC0 /* The address must be even */ && (!((unsigned char)*(unsigned long*)prgm & 1))) { /* Test if it is a 'move.l #$400xx,an' or 'lea.l $400xx,an'. It's true * that perhaps we are not testing the 2 first bytes of the instructions, * and in the following tests we will sometimes test he code of the * instruction before, but we cannot integrate a real disassembler... */ if ((*(short*)(prgm - 2) & 0xf1ff) == 0x207c || (*(short*)(prgm - 2) & 0xf1ff) == 0x41f9) { /* If the next instruction is an adda an,am or adda am,an with the same an, special-case it. */ if ((2[(short*)prgm] & 0xf1f8) == 0xd1c8 && ((((-1)[(short*)prgm] >> 9) & 7) == ((2[(short*)prgm]) & 7) || (((-1)[(short*)prgm] >> 9) & 7) == ((2[(short*)prgm] >> 9) & 7))) { *((unsigned long*)prgm)++ -= 0x40000; patches_num++; continue; } else { PatchGhost: /* Now Read / Write to 0x200000 - 0x23FFFF */ *((unsigned long*)prgm)++ += 0x200000 - 0x40000; patches_num++; continue; } } /* Test if it is 'move.l $400xx,?' */ if ((*(short*)(prgm - 2) & 0b1111000000111111) == 0b0010000000111001) { goto PatchGhost; } /* Test if it is 'move.l ?,$400xx'. $400xx is the destination here, so * it's a bit more complicated since the source may have an offset or * an absolute value between the opcode and $400xx. */ /* move.l [dn/an,$] [-(an)+,$] - Keep the Mode Code of the destination. */ opcode = *(short*)(prgm - 2) & 0b1111111111111000; /* The MSB of the Mode Code is not set for modes without an offset or * an absolute value, except for -(an) (thank you Motorola :)) */ if ( /* -(an) */ opcode == 0b0010001111100000 || /* keep only the MSB and check it is cleared */ (opcode & 0b1111111111100111) == 0b0010001111000000) { goto PatchGhost; } /* move.l [X(pc)] [X(an)] - Jump the offset. */ opcode = *(short*)(prgm - 4); if ( /* X(pc) */ (opcode & 0b1111111111111000) == 0b0010001111101000 || /* X(an) */ opcode == 0b0010001111111010 ) { goto PatchGhost; } /* move.l [($X).l] [#X] - Jump the address. */ opcode = *(short*)(prgm - 6); if ( /* $X */ opcode == 0b0010001111111001 || /* #X */ opcode == 0b0010001111111100) { goto PatchGhost; } } /* 'looks like an access to the RAM's ghost space' */ /* Detect enter_ghost_space routines and disable them. The ghosting of the return address before returning will be disabled by the next series of patches. */ /* First version (Feb 2001) */ else if (!memcmp(prgm,(unsigned long long []){0x207800c80ca80000ull, 0x03e8fffc656248e7ull,0xfffe4fefffec267cull,0x0003e0003f132f2bull}, 32)) { *(short*)prgm=0x606e; patches_num++; } /* Newer versions (Jan 2002 and later, also used by current h220xTSR) */ else if (!memcmp(prgm,(unsigned long []){0x61000002ul,0x201f0c80ul, 0x00040000ul},12)) { prgm[12]=0x60; patches_num++; } /* EXECUTE_IN_GHOST_SPACE */ else if (*(short*)prgm==0x41fa && !memcmp(prgm+4,(unsigned long []) {0x20080c80ul,0x00040000ul},8)) { prgm[12]=0x60; patches_num++; } /* Another variant was used in h220xTSR 1.05 and earlier, but h220xTSR won't even bother calling it if the hardware version is not 2, so don't bother patching it. */ /* The TIGCC DLL support checks if we are executing in ghost space. Disable this check. */ else if (!memcmp(prgm,(unsigned short[]){0x6100,0x0002,0x201f,0x7201,0x0c80, 0x0003,0xffff},14)) { *(long*)(prgm+10)=0; patches_num++; } /* The FAT Engine checks if we are executing in ghost space. Try to disable this check. (It may fail because it is compiled from a header. Ugh!) */ else if (!memcmp(prgm,(unsigned long []){0x61000002ul,0x201f42b9ul},8) &&!memcmp(prgm+12,(unsigned long []){0x72ff0c80ul,0x3fffful},8)) { 4[(long*)prgm]=0; patches_num++; } /* Look for the following classes of instructions: adda.l #0x3fffe-#0x40fff,%an addi.l #0x3fffe-#0x40fff,%dn ori.l #0x40000,%dn or.l #0x40000,(%a0) (used in enter_ghost_space in TIGCC 0.95) bset.l #18,%dn;movea.l %dn,%an move.l/lea.l #0x3fffe-0x3ffff,%an move.l #0x3fffe-0x40010,%dn move.l x(%pc),%an; bset.b #2,1(%an) (used for __save_sp__ in h220xTSR) and unghost them. */ /* adda.l #0x3fffe-#0x40010,%an */ else if ((*(short*)prgm&0xf1ff)==0xd1fc && *(long*)(prgm+2)>=0x3fffe && *(long*)(prgm+2)<=0x40fff) { *(long*)(prgm+2)-=0x40000; patches_num++; } /* addi.l #0x3fffe-#0x40010,%dn */ else if ((*(short*)prgm&0xfff8)==0x0680 && *(long*)(prgm+2)>=0x3fffe && *(long*)(prgm+2)<=0x40fff) { *(long*)(prgm+2)-=0x40000; patches_num++; } /* ori.l #0x40000,%dn */ else if ((*(short*)prgm&0xfff8)==0x0080 && *(long*)(prgm+2)==0x40000) { *(long*)(prgm+2)=0; patches_num++; } /* ori.l #0x40000,(%a0) (yes, only %a0) */ else if (*(short*)prgm==0x0090 && *(long*)(prgm+2)==0x40000) { *(long*)(prgm+2)=0; patches_num++; } /* bset.l #18,%dn;movea.l %dn,%an */ else if ((*(short*)prgm&0xfff8)==0x08c0 && 1[(short*)prgm]==18 && (2[(short*)prgm]&0xf1f8)==0x2040) { *(long*)prgm=0x4e714e71; patches_num++; } /* move.l/lea.l #0x3fffe-0x3ffff,%an */ else if (((*(short*)prgm&0xf1ff) == 0x207c || (*(short*)prgm&0xf1ff) == 0x41f9) && (*(long*)(prgm+2)&(~1L))==0x3fffe) { *(long*)(prgm+2)-=0x40000; patches_num++; } /* move.l #0x3fffe-#0x40010,%dn */ else if ((*(short*)prgm&0xf1ff)==0x203c && *(long*)(prgm+2)>=0x3fffe && *(long*)(prgm+2)<=0x40010) { *(long*)(prgm+2)-=0x40000; patches_num++; } /* move.l x(%pc),%an; bset.b #2,1(%an) (with the same %an) */ else if ((*(short*)prgm&0xf1ff)==0x207a && (2[(short*)prgm]&0xfff8)==0x08e8 && *(long*)(prgm+6)==0x00020001 && ((*(short*)prgm >> 9) & 7) == ((2[(short*)prgm]) & 7)) { 1[(long*)prgm]=0x4e714e71; 4[(short*)prgm]=0x4e71; patches_num++; } /****************************************************************************** * Other patches can be added below. *****************************************************************************/ /* If all the tests have failed */ prgm += sizeof(short); } /* 'for all the words of the program' */ /* The patches for the calculator detection routines cannot be applied in the * loop above, because the routine does not work word by word. */ patches_num += PatchRomBase(prgm_head, prgm_tail); /* Other calls to patching routines can be added below. */ return patches_num; }