;Install routines include "settings.inc" include "ti83plus.inc" include "equates.inc" SEGMENT Main GLOBALS ON EXTERN ExitApp,PutSApp,DisplayPressAnyKey,StartApp,UnlockFlash,translatePage,cmpstrb,cphlde,MemClear EXTERN InvalidatePatchData,WriteBlockData,DisplayProgress,GetOSCompatibilityBlock,LoadConfigFromStorageArea EXTERN appChangeHook,catalog1Hook,catalog2Hook,cursorHook,fontHook,getKeyHook,graphHook,graphicsHook EXTERN helpHook,homescreenHook,libraryHook,linkActivityHook,localizeHook,menuHook,parserHook EXTERN rawKeyHook,regraphHook,silentLinkHook,tokenHook,traceHook,USBActivityHook,windowHook EXTERN yEqualsHook,cxRedispHook Var searchLoc,2 Var swapSectorPage,1 Var bankedCallAddress,2 InstallHookMan: B_CALL ClrLCDFull B_CALL HomeUp call IsInstalled jr nc,alreadyInstalled ld hl,sInstalling call PutSApp ld hl,plotSScreen B_CALL SaveDisp ;Install the OS patch call InstallOSPatch ;Install all the hooks we have chain blocks for call LoadConfigFromStorageArea ;Display success B_CALL ClrLCDFull B_CALL HomeUp ld hl,sSuccess call PutSApp $$: call DisplayPressAnyKey B_CALL GetKey jr StartApp alreadyInstalled: ld hl,sAlreadyInstalled call PutSApp jr $B IsInstalled: ;Returns carry flag set if NOT installed ld hl,newPage0CallAddr inc (hl) scf ret z ;Okay, something was written to page 0, so we might be installed ;Do we have an OS compatibility block stored? call GetOSCompatibilityBlock ret c ;no, abort inc hl inc hl inc hl ld d,(hl) inc hl ld e,(hl) push de B_CALL GetBaseVer pop de cp d scf ret nz ;OS major version not right ld a,b cp e scf ret nz ;OS minor version not right or a ret sInstalling: DB "Installing",0CEh," " DB " " DB "Be patient; this" DB "WILL take a few " DB "moments.",0 sSuccess: DB "HookMan has been" DB "successfully " DB "installed!",0 sAlreadyInstalled: DB "HookMan appears " DB "to have already " DB "been installed! " DB "Try uninstalling" DB "first, or try to" DB "resend the OS.",0 InstallOSPatch: ;Get the call address for making page 0 calls on this OS version ld a,7Bh call translatePage ld hl,44F2h ld de,bankedCallAddress ld bc,2 B_CALL FlashToRam call UnlockFlash ld a,10 call DisplayProgress ;Scan the entire OS (pages 0-7 and either 74h-7Dh or 78h-7Dh) for the following: ; ld hl,8000h ; ld de,8001h ; ld bc,XXXX ;generic value ; ld (hl),0 ; ldir ;Also scan for: ; ld hl,9BD0h ; ld de,9BD1h ; ld bc,XXXX ;generic value ; ld (hl),0 ; ldir ;Also scan for: ; ld de,XXXX ;generic value ; call YYYY ;generic value ; jr nz,XXXX ;generic value ; Also make sure the data at YYYY is: ; ld hl,0 ; or a ; sbc hl,de ; ex de,hl ; push de ; push hl ;Each time one is found, write the following entry to RAM (starting at savesscreen): ; DW wSizeOfBlock ;this is the data size + 3 ; DW wPatchAddress ; DB bPatchPage ; DB bOriginalData ;wSizeOfBlock-3 bytes ;Copy the pattern-finding code to RAM ld hl,findPatternStart ld de,appBackUpScreen ld bc,findPatternEnd-findPatternStart ldir res extendedSearch,(iy+hookManFlags) ;Reset the patch block pointer ld hl,saveSScreen ld (iMathPtr2),hl ;Get information for first RAM-clearing pattern (boot) ld ix,searchPattern1-findPatternStart+appBackUpScreen call GetPatchBlocks ld a,25 call DisplayProgress ;Get information for second RAM-clearing pattern (MEM reset) ld ix,searchPattern2-findPatternStart+appBackUpScreen call GetPatchBlocks ld a,50 call DisplayProgress ;Get information for third RAM-clearing pattern (self-test) set extendedSearch,(iy+hookManFlags) ld ix,searchPattern3-findPatternStart+appBackUpScreen call GetPatchBlocks ld a,75 call DisplayProgress ;Add the two additional patch blocks so they get sorted along with the others. ;This is only used for the uninstall, so it knows what to revert back to FF's. ld hl,(iMathPtr2) ld (hl),patchAdditional1 inc hl ld de,replacementCodeEnd-replacementCode push de pop bc inc de ;add address bytes inc de inc de ;add page byte ld (hl),e inc hl ld (hl),d inc hl ld (hl),replacementCodeAddr & 0FFh inc hl ld (hl),replacementCodeAddr >> 8 inc hl ld (hl),replacementCodePage inc hl push hl pop de inc de ld (hl),0FFh push bc dec bc ldir pop bc ld hl,(iMathPtr2) inc bc ;skip past patch ID inc bc ;skip past data block size inc bc inc bc ;skip past address inc bc inc bc ;skip past page add hl,bc ;Okay, now we're ready to put the second additional block. push hl ld (hl),patchAdditional2 inc hl ld (hl),9 ;six bytes for the page 0 call, plus page and address inc hl ld (hl),0 inc hl ld (hl),newPage0CallAddr & 0FFh inc hl ld (hl),newPage0CallAddr >> 8 inc hl ld (hl),0 inc hl push hl pop de inc de ld (hl),0FFh ld bc,5 ldir pop hl ld bc,12 ;patch ID + data block size + address + page + 6 bytes of data add hl,bc ;Put the OS compatibility block (so we know which OS version this patch information belongs to). push hl B_CALL GetBaseVer pop hl ld (hl),OSCompatibilityID inc hl ld (hl),2 inc hl ld (hl),0 inc hl ld (hl),a ;dummy address 1 inc hl ld (hl),b ;dummy address 2 inc hl ;Terminate the patch block group. ld (hl),0FFh ;Re-order all of the patch blocks by page and address. ld hl,saveSScreen+384 ld (iMathPtr3),hl ;new destination startSort: ld hl,saveSScreen ld (iMathPtr5),hl ;minimum ld a,(hl) inc a jr z,sortDone call GetNextBlock jr c,minimumLoopDone ld (iMathPtr4),hl findMinimumLoop: ld hl,(iMathPtr5) ld de,(iMathPtr4) call comparePatchBlocks jr nc,$F ld de,(iMathPtr4) ld (iMathPtr5),de $$: ld hl,(iMathPtr4) call GetNextBlock jr c,minimumLoopDone ld (iMathPtr4),hl jr findMinimumLoop minimumLoopDone: ;The minimum address patch block is at (iMathPtr5) ;Write it to (iMathPtr3) and advance that pointer ld hl,(iMathPtr5) ld de,(iMathPtr3) call CopyPatchBlock ld (iMathPtr3),de ;Shift everything back at (iMathPtr5) push hl add hl,bc pop de ;... => ... ;In above, DE points to old block and HL points to new block ld bc,192 ldir jr startSort sortDone: ld hl,(iMathPtr3) ld (hl),0FFh ;Move the sorted copy back to saveSScreen ld hl,saveSScreen+384 ld de,saveSScreen ld bc,384 ldir ld a,80 call DisplayProgress ;This is the monster call that does (or reverses) the patching to the OS based on the blocks in saveSScreen set isInstalling,(iy+hookManFlags) ;apply our custom code, not the original code from the patch blocks call ApplyPatchBlocks ld a,90 call DisplayProgress ; Once all the patch blocks have been parsed, write the replacement code to page 7Bh. ; I'm kind of taking a gamble on whether there's enough space on 7Bh, but I think there's plenty on any OS version. ; This can be made dynamic if necessary. ld hl,replacementCode ld de,appData ld bc,replacementCodeEnd-replacementCode ldir ld a,replacementCodePage ld hl,appData ld de,replacementCodeAddr ld bc,replacementCodeEnd-replacementCode B_CALL WriteFlash ; Now write the new page 0 call. call SetUpNewPage0Code xor a ld hl,OP1 ld de,newPage0CallAddr ld bc,6 B_CALL WriteFlash ; Invalidate any existing patch blocks in application by changing the ID to 0. call InvalidatePatchData ; Write the patch blocks to the application, defragmenting if necessary. jr WriteBlockData replacementCode: push af push bc push de push hl ;Okay, let's erase everything from 8000h to 0FFFFh, minus the hook stuff ld b,(iy+33h) ld c,(iy+34h) ld d,(iy+35h) ld e,(iy+36h) push bc push de ld hl,8000h ld de,8001h ld (hl),0 ld bc,9B78h-8000h-1 ldir ld hl,9BD8h ld de,9BD8h+1 ld (hl),0 ld bc,0FDFAh-9BD8h ldir pop de pop bc ld (iy+33h),b ld (iy+34h),c ld (iy+35h),d ld (iy+36h),e pop hl pop de pop bc pop af ret replacementCodeEnd: SetUpNewPage0Code: ld hl,OP1 ld (hl),0CDh inc hl ld bc,(bankedCallAddress) ld (hl),c inc hl ld (hl),b inc hl ld (hl),replacementCodeAddr & 0FFh inc hl ld (hl),replacementCodeAddr >> 8 inc hl ld (hl),replacementCodePage ret CopyPatchBlock: ;Returns total block size in BC inc hl ;Now at size ld c,(hl) inc hl ld b,(hl) dec hl dec hl inc bc inc bc inc bc ;HL is start of patch block ;DE is destination ;BC is size of entire block push bc push hl ldir pop hl pop bc ret comparePatchBlocks: inc hl ;Now at size 1 inc hl ;Now at size 2 inc hl ;Now at address 1 inc hl ;Now at address 2 inc hl ;Now at page inc de ;Now at size 1 inc de ;Now at size 2 inc de ;Now at address 1 inc de ;Now at address 2 inc de ;Now at page ld a,(de) cp (hl) ret nz push de push hl dec hl ld b,(hl) dec hl ld c,(hl) ex de,hl dec hl ld d,(hl) dec hl ld e,(hl) push bc pop hl ;HL is the address from the HL block ;DE is the address from the DE block B_CALL CpHLDE ccf pop hl pop de ret GetNextBlock: push de ld d,(hl) inc d scf jr z,$F ;there is no next block inc hl ld e,(hl) inc hl ld d,(hl) inc hl add hl,de ld d,(hl) inc d scf jr z,$F ;there is no next block or a $$: pop de ret GetPatchBlocks: ld hl,pageTable ld (iMathPtr3),hl ld de,4000h ld (searchLoc),de patternSearchLoop: ld hl,(iMathPtr3) ld a,(hl) cp 7 jr c,$F cp 78h jr nc,$F in a,(2) and 80h jr z,skipThisPage $$: ld de,(searchLoc) call translatePage call appBackUpScreen ld de,(searchLoc) inc de ld (searchLoc),de ;increase the search pointer so we'll find something new next time jr nz,skipThisPage ;we're done with this page ;We found a pattern, but are we sure this is right? bit extendedSearch,(iy+hookManFlags) jr z,$F ;Let's make sure this is really right ld hl,(iMathPtr3) ld a,(hl) ld hl,(iMathPtr1) ld de,4 add hl,de ld de,OP1 ld bc,2 B_CALL FlashToRam ;The address to check is now at (OP1) ld hl,(iMathPtr3) ld a,(hl) ld hl,(OP1) ld de,OP2 ld bc,verifyBlockEnd-verifyBlock B_CALL FlashToRam ld hl,verifyBlock ld de,OP2 ld b,verifyBlockEnd-verifyBlock call cmpstrb jr nz,$B ;no, this actually wasn't a match, so start the search over $$: ;We found a pattern, so let's save the original data in a patch block ld hl,(iMathPtr2) ;First put the patch ID ld a,(ix-2) ld (hl),a inc hl ;Now put the size of this data block ld b,0 ld c,(ix-1) inc bc ;original code size + address + page inc bc inc bc ld (hl),c inc hl ld (hl),b inc hl ;Now put the address we found ld de,(iMathPtr1) ld (hl),e inc hl ld (hl),d inc hl ;Now put the page it was found on ld bc,(iMathPtr3) ld a,(bc) ld (hl),a inc hl ex de,hl ;Now copy the original code to our patch block ld hl,(iMathPtr1) ld bc,(iMathPtr3) ld a,(bc) ld b,0 ld c,(ix-1) B_CALL FlashToRam ld hl,(iMathPtr2) ld b,0 ld c,(ix-1) inc bc inc bc inc bc inc bc inc bc inc bc add hl,bc ld (iMathPtr2),hl skipThisPage: ld hl,(iMathPtr3) inc hl ld (iMathPtr3),hl ld a,(hl) inc a jr nz,patternSearchLoop ret pageTable: DB 0,1,2,3,4,5,6,7,74h,75h,76h,77h,78h,79h,7Ah,7Bh,7Ch,7D,0FFh verifyBlock: ld hl,0 or a sbc hl,de ex de,hl push de push hl verifyBlockEnd: findPatternStart: ;Find pattern in IX, Flash page in A, starting address in DE ;Returns NZ if pattern not found ;(iMathPtr1) contains the address of match found ;Search pattern: terminated by 0FEh ; 0FFh is ? (one-character wildcard) ld (iMathPtr4),a in a,(6) push af ld hl,findPatternRet-findPatternStart+appBackUpScreen push hl ld a,(iMathPtr4) out (6),a dec de searchLoopRestart: inc de ld (iMathPtr1),de push ix pop hl searchLoop: ld b,(hl) ld a,b inc a inc a ret z inc de dec a jr z,$F dec de ld a,(de) inc de bit 7,d ret nz cp b jr z,$F ld de,(iMathPtr1) jr searchLoopRestart $$: inc hl jr searchLoop findPatternRet: pop bc ld a,b out (6),a ret DB patch1ID DB searchPattern1End-searchPattern1 searchPattern1: ld hl,8000h ld de,8001h ld bc,0FFFFh ;generic value ld (hl),0 ldir searchPattern1End: DB 0FEh DB patch2ID DB searchPattern2End-searchPattern2 searchPattern2: ld hl,9BD0h ld de,9BD1h ld bc,0FFFFh ;generic value ld (hl),0 ldir searchPattern2End: DB 0FEh DB patch3ID DB searchPattern3End-searchPattern3 searchPattern3: ld de,0FFFFh ;generic value call 0FFFFh ;generic value DB 20h ;jr nz, DW 0FFFFh ;generic value searchPattern3End: DB 0FEh findPatternEnd: ApplyPatchBlocks: ;Parse through the group of patch blocks: ; Erase the swap sector. ld a,(swapSectorPage) B_CALL EraseFlashPage ;Set up BCALL replacement routine (since we can't rely on the OS being intact from here on out) ;Actually, the OS is still there, I don't know why I wrote any of this...but oh well. ld hl,BCALLroutine ld de,appBackUpScreen ld bc,BCALLroutineEnd-BCALLroutine ldir ; Loop: ; Get the sector containing the page for the first patch block. ; For each page in the sector: ; Copy each byte from the page to the corresponding page in the swap sector, until patch block's address is found. ; When patch block's address is found: ; Skip copying that many bytes. Write the patched code instead. ; Move to the next patch block. ld hl,saveSScreen ld (iMathPtr5),hl copySectorsLoop: ld a,4 ld (iMathPtr1+1),a ld a,(swapSectorPage) ld (iMathPtr2+1),a call GetPatchBlockInfo and 0FCh ld (iMathPtr4),a copyPagesLoop: call GetPatchBlockInfo ;(iMathPtr5) is the patch block we're currently looking for ;(iMathPtr2):(iMathPtr3) contains the page:address we need to find ;(iMathPtr4) is the current sector we're writing to ;(iMathPtr4+1) is the patch ID (patch1, patch2, patch3) ;(iMathPtr2+1) is the current page we're copying to in the swap sector ;(iMathPtr1+1) is number of pages left in the sector to copy ld a,(iMathPtr4) ld b,a ld de,4000h copyPageLoop: ;Copy from page B to page (iMathPtr2+1) unless it's one of our patch addresses ;Does BDE match the patch block's page:address? push de ld a,(iMathPtr2) cp b jr nz,$F ld hl,(iMathPtr3) call cphlde jr nz,$F ;This is the patch block address, so write our own code instead ;B must be preserved; DE must be advanced to next address push bc ld hl,OP1 ld bc,66 push de call MemClear pop de ld a,(iMathPtr4+1) bit isInstalling,(iy+hookManFlags) jr nz,skipAdditionalPatches cp patchAdditional1 jr z,writeOriginalCode ;this is just writing FF's to FF's, so I think this is fine cp patchAdditional2 jr z,writeOriginalCode ;this is just writing FF's to FF's, so I think this is fine skipAdditionalPatches: cp patch1ID jr z,doPatch1 cp patch2ID jr z,doPatch2 cp patch3ID pop bc jr nz,$F push bc ;Do patch 3 bit isInstalling,(iy+hookManFlags) jr z,writeOriginalCode ;We're just NOPing out this part, so leave OP1-OP6 as zeroes ld hl,OP1 ld a,(iMathPtr2+1) ld bc,searchPattern3End-searchPattern3 ;A is the Flash page we're writing to ;HL contains the replacement code (all zeroes) ;DE points to where we're writing to ;BC contains the number of bytes ld ix,_WriteFlash-4000h call appBackUpScreen pop bc pop hl ;Update DE to point past what we just wrote ld de,searchPattern3End-searchPattern3 add hl,de ex de,hl ld hl,(iMathPtr5) call GetNextBlock ld (iMathPtr5),hl ld a,0FFh ld (iMathPtr2),a ;in case we're at the end (carry set), set the page to 0FFh so future searches will fail jr copyPageLoop writeOriginalCode: ld hl,(iMathPtr5) inc hl ;now at block size 1 ld c,(hl) inc hl ;now at block size 2 ld b,(hl) dec bc ;decrease for page dec bc ;decrease for address 1 dec bc ;decrease for address 2 inc hl ;now at address 1 inc hl ;now at address 2 inc hl ;now at page inc hl ;now at original OS code ld a,(iMathPtr2+1) ;A is the Flash page we're writing to ;HL contains the original OS code ;DE points to where we're writing to ;BC contains the number of bytes push bc ld ix,_WriteFlash-4000h call appBackUpScreen pop de pop bc pop hl ;Update DE to point to what we just wrote add hl,de ex de,hl ld hl,(iMathPtr5) call GetNextBlock ld (iMathPtr5),hl ld a,0FFh ld (iMathPtr2),a ;in case we're at the end (carry set), set the page to 0FFh so future searches will fail call nc,GetPatchBlockInfo jr copyPageLoop doPatch1: bit isInstalling,(iy+hookManFlags) jr z,writeOriginalCode call SetUpReplacementCode ld hl,OP1 ld a,(iMathPtr2+1) ld bc,searchPattern1End-searchPattern1 ;A is the Flash page we're writing to ;HL contains the replacement code ;DE points to where we're writing to ;BC contains the number of bytes ld ix,_WriteFlash-4000h call appBackUpScreen pop bc pop hl ;Update DE to point past what we just wrote ld de,searchPattern1End-searchPattern1 add hl,de ex de,hl ld hl,(iMathPtr5) call GetNextBlock ld (iMathPtr5),hl ld a,0FFh ld (iMathPtr2),a ;in case we're at the end (carry set), set the page to 0FFh so future searches will fail call nc,GetPatchBlockInfo jr copyPageLoop doPatch2: bit isInstalling,(iy+hookManFlags) jr z,writeOriginalCode call SetUpReplacementCode ld hl,OP1 ld a,(iMathPtr2+1) ld bc,searchPattern2End-searchPattern2 ;A is the Flash page we're writing to ;HL contains the replacement code ;DE points to where we're writing to ;BC contains the number of bytes ld ix,_WriteFlash-4000h call appBackUpScreen pop bc pop hl ;Update DE to point past what we just wrote ld de,searchPattern2End-searchPattern2 add hl,de ex de,hl ld hl,(iMathPtr5) call GetNextBlock ld (iMathPtr5),hl ld a,0FFh ld (iMathPtr2),a ;in case we're at the end (carry set), set the page to 0FFh so future searches will fail jr copyPageLoop $$: pop de ld h,d ld l,e ld a,b ld ix,_LoadAIndPaged-4000h call appBackUpScreen push bc push de ld b,a ld a,(iMathPtr2+1) ld ix,_WriteAByte-4000h call appBackUpScreen pop de pop bc inc de bit 7,d jr z,copyPageLoop ld hl,iMathPtr4 inc (hl) ;increase the page we're reading from ld hl,iMathPtr2+1 inc (hl) ;increase the swap sector page we're writing to ld hl,iMathPtr1+1 dec (hl) ;are we done with this sector yet? jr nz,copyPagesLoop ; Erase the original sector, copy the swap sector to it, and erase the swap sector. ld a,(iMathPtr4) dec a and 0FCh ld ix,_EraseFlashPage-4000h call appBackUpScreen ld a,(iMathPtr4) dec a and 0FCh ld b,a ld a,(swapSectorPage) call CopySector ld a,(swapSectorPage) ld ix,_EraseFlashPage-4000h call appBackUpScreen ;Are we done with all the patch blocks? ld hl,(iMathPtr5) call GetNextBlock ld (iMathPtr5),hl jr nc,copySectorsLoop ret CopySector: ;Relies on BCALL replacement routine at appBackUpScreen ld c,4 CopySector_Page: ld hl,4000h push af push bc CopySector_PageLoop: push af push bc ld ix,_LoadAIndPaged-4000h call appBackUpScreen ld b,a pop af push af ;A is now the destination page ;B is the byte to write ex de,hl push de ;DE is the destination ld ix,_WriteAByte-4000h call appBackUpScreen pop hl inc hl pop bc pop af bit 7,h jr z,CopySector_PageLoop pop bc pop af inc a inc b dec c jr nz,CopySector_Page ret SetUpReplacementCode: ld hl,OP1 ld (hl),0CDh inc hl ld (hl),newPage0CallAddr & 0FFh inc hl ld (hl),newPage0CallAddr >> 8 ret BCALLroutine: ;Calls boot code routine ;Pass IX as the address (subtract 4000h first) ld (9C87h),a in a,(6) push af ld a,7Fh push hl push bc call callTranslatePage-BCALLroutine+appBackUpScreen pop bc out (6),a ld l,(ix+0) ld h,(ix+1) push hl pop ix pop hl ld a,(9C87h) call jpIX-BCALLroutine+appBackUpScreen ld (9C87h),a pop af out (6),a ld a,(9C87h) ret jpIX: jp (ix) callTranslatePage: ld b,a in a,(2) and 80h jr z,$F in a,(21h) and 3 ld a,b ret nz and 3Fh ret $$: ld a,b and 1Fh ret BCALLroutineEnd: GetPatchBlockInfo: ;Returns A as page push de push bc ld hl,(iMathPtr5) ld a,(hl) ld (iMathPtr4+1),a ;patch ID (patch1, patch2, patch3) inc hl ;Now at size 1 ld c,(hl) inc hl ;Now at size 2 ld b,(hl) inc hl ;Now at address 1 ld e,(hl) inc hl ld d,(hl) ld (iMathPtr3),de inc hl ld a,(hl) ld (iMathPtr2),a pop bc pop de ret