;IOHook v0.02a ;Brandon Wilson ;This is a very nasty OS patch which allows an SE/84+ calculator to intercept OS requests to send or receive bytes through I/O ; by adding what I call an "I/O hook." ;This opens up the possibilities of transferring variables, certificate revisions, etc. to yourself, as well as I/O logging ; for debugging/development purposes. ;This is more a theory than anything, but I think it can work. ;Here is the hook documentation: ;I/O Link Hook: ;Hook block: 9BD8h ;Hook active flag: 1,(iy+3Ah) ;This hook is called when bytes are sent and/or received over the I/O link port. ;B=1: The calculator is about to send a byte. The byte is in A. You can change it if you want. Return NZ to cancel sending. ; This event fires when _SendAByte/_SendCByte are first called. ;B=2: The calculator is finishing the receive of a byte from the silent link. ; This will be the first byte from a silent link packet. You can return NZ and the byte in A to "fake" receiving whatever you want. ; This event fires when _ContinueGetByte is first called. ;B=3: The calculator has started waiting to receive a byte. This is usually the first byte in a link transmission or packet. ; You can return NZ and the byte in A to "fake" receiving whatever you want. ; This event fires when _Rec1stByte/_Rec1stByteNC are first called. ;B=4: The calculator is receiving a byte. You can return NZ and the byte in A to "fake" receiving whatever you want. ; This event fires when _RecAByteIO is first called. ;B=5: The calculator is sending a 4-byte packet. You can return NZ to cancel sending, but don't unless you have a really good reason. ; This event fires when _Send4Bytes is first called. ;B=6: The calculator has finished receiving the first byte from the silent link detection. This will be the first byte from a silent ; link packet. You can change the value of the byte received in A. ; This event fires when _ContinueGetByte is returning. ;B=7: The calculator has finished receiving the first byte of a link transmission or packet. You can change the value of the byte ; received in A. ; This event fires when _Rec1stByte/_Rec1stByteNC is returning. ;B=8: The calculator has finished receiving a byte over I/O. You can change the value of the byte received in A. ; This event fires when _RecAByteIO is returning. ;Keep in mind that this is not your typical OS hook, and it has no OS support...so if you have an application with a hook like this, ; and a defragment happens, the hook is no longer valid. ; It's meant to be more of a temporary "I can finally make the OS do this just this one time" kind of thing. ;v0.02a Changelog ;Added I/O logging (to RAM pages 84h-87h) and log viewer ;Added demos section to demonstrate the ability to transfer to yourself ;v0.01a Changelog ;Initial release include "settings.inc" NOLIST include "ti83plus.inc" LIST include "equates.inc" include "header.asm" SEGMENT MAIN GLOBALS ON EXTERN PutSApp,IGetKey,vputstring,VStrLen,VPutSAppCenter,DispHexA,GetHexA,SoftKey,ParseInp,invert_lines,ifastcopy,UnlockFlash EXTERN translatePage,newSectorStart,newSectorEnd,startLog,stopLog,viewLog,AnimateRunIndicator,selfTransfer Var swapSectorPage,1 Var offPageJumpAddress,2 ;------------------------------- ;DO NOT REORDER THESE Var SendCByteAddress,3 Var SendCByteBlock,6 Var ContinueGetByteAddress,3 Var ContinueGetByteBlock,6 Var Rec1stByteNCAddress,3 Var Rec1stByteNCBlock,6 Var RecAByteIOAddress,3 Var RecAByteIOBlock,6 Var Send4BytesAddress,3 Var Send4BytesBlock,6 ;------------------------------- Var srcPage,1 Var destPage,1 ;------------------------------- numMatches equ saveSScreen+2 matches equ numMatches+1 StartApp: in a,(2) and 80h jr nz,$F ld a,E_Version B_JUMP JError $$: B_CALL ClrLCDFull B_CALL DelRes B_CALL HomeUp ld hl,sTitle call PutSApp B_CALL newline ld hl,sMenu call PutSApp keyLoop: B_CALL GetKey cp k1 jr z,installIOHook cp k2 jr z,uninstallIOHook cp k3 jr z,startLog cp k4 jr z,stopLog cp k5 jr z,viewLog cp k6 jr z,viewDemos cp k7 jr z,ExitApp cp kClear jr z,ExitApp cp kQuit jr nz,keyLoop ExitApp: B_CALL ClrLCDFull B_JUMP JForceCmdNoChar viewDemos: B_CALL ClrLCDFull B_CALL HomeUp ld hl,sDemos call PutSApp demosKeyLoop: B_CALL GetKey cp kQuit jr z,ExitApp cp k1 jr z,selfTransfer cp k2 jr z,ExitApp cp kClear jr nz,demosKeyLoop jr StartApp sTitle: DB "IOHook ",VER_STRING,0 sMenu: DB " 1) Install " DB " 2) Uninstall " DB " 3) Start Log " DB " 4) Stop Log " DB " 5) View Log " DB " 6) Demos " DB " 7) Quit",0 sDemos: DB "Demos " DB "1) Self Transfer" DB "2) Back",0 sInstalling: DB "Installing",0CEh,0 sUninstalling: DB "Uninstalling",0CEh,0 sSuccessful: DB "Successful! " DB "Press any key",0CEh,0 sNotInstalled: DB "Not installed! " DB "Press any key",0CEh,0 sAlreadyInstalled: DB "Already " DB "installed! " DB "Press any key",0Ceh,0 IsInstalled: ld a,70h call translatePage ld hl,4000h B_CALL LoadAIndPaged inc a scf ret z or a ret installIOHook: B_CALL ClrLCDFull B_CALL HomeUp ;Are we already installed? call IsInstalled jr c,$F ld hl,sAlreadyInstalled call PutSApp B_CALL GetKey jr StartApp $$: ;Display status ld hl,sInstalling call PutSApp ;Unlock Flash call UnlockFlash ;Look up and save patch addresses for: ; _SendCByte ; _ContinueGetByte (3 bytes AFTER starting address for this one) ; _Rec1stByteNC ; _RecAByteIO ; _Send4Bytes call GetVectorInfo ;Look up and save address for _OffPageJump ld a,7Bh call translatePage ld hl,_OffPageJump ld de,offPageJumpAddress ld bc,2 B_CALL FlashToRam ;Erase sector 30h/70h ld a,70h call translatePage B_CALL EraseFlashPage ;Write new code to sector 30h/70h ;I can't figure out the right way to do this in ZDS, so I'm just going to write ; most of this application to sector 70h (can't hurt) ld hl,4080h ld de,4080h ld bc,3F00h-80h call copyMyself ld hl,newSectorStart ld de,4000h ld bc,5*3 call copyMyself ;---------------------------------------------------------- ;The below will not be undone, OS must be resent ;Patch OS to not check sector 30h/70h on reset ;This is just code by TI to screw us over, it won't hurt to ; leave it patched call PatchSector70hCode ;---------------------------------------------------------- ;Write original 6-byte block for each routine to: ; 70:7F00h ; 70:7F06h ; 70:7F0Ch ; 70:7F12h ; 70:7F18h ld a,70h call translatePage ld hl,SendCByteBlock ld de,7F00h ld b,5 $$: push bc ld bc,6 push af push hl B_CALL WriteFlash pop hl ld bc,9 add hl,bc pop af pop bc call AnimateRunIndicator djnz $B ;Erase swap sector B_CALL FindSwapSector ld (swapSectorPage),a B_CALL EraseFlashPage ;Find and store all instances of B_CALL GetByteOrBoot call FindGetByteOrBoot ;Selectively copy page 3Ch/7Ch to swap sector except for 6-byte blocks ; for each routine call copyPartialPage ;Copy page 3Dh/7Dh to second page of swap sector ld a,7Dh call translatePage ld bc,(swapSectorPage-1) inc b B_CALL CopyFlashPage ;Write call (_OffPageJump address) \ .dw xxxx \ .db 70h for ; each routine, starting at 70:4000h and increasing by 3 each time ld a,70h call translatePage ld b,5 ld ix,SendCByteBlock ld hl,4000h $$: push bc ld (ix+0),0CDh ld de,(offPageJumpAddress) ld (ix+1),e ld (ix+2),d ld (ix+3),l ld (ix+4),h ld (ix+5),a ld de,9 add ix,de ld de,3 add hl,de pop bc call AnimateRunIndicator djnz $B call writeBlocks ;Write _RecAByteIO to all instances of _GetByteOrBoot ld hl,appData ld (hl),0EFh inc hl ld (hl),03h inc hl ld (hl),4Fh ld bc,(numMatches-1) ld a,b or a jr z,skipGetByteOrBoot ld c,0 $$: push bc ld h,0 ld l,c add hl,hl ld bc,matches add hl,bc ld e,(hl) inc hl ld d,(hl) ld bc,3 ld hl,appData ld a,(swapSectorPage) B_CALL WriteFlash pop bc inc c call AnimateRunIndicator djnz $B skipGetByteOrBoot: ;---------------------------------- ;The below must be done without OS ;Erase sector 3Ch/7Ch ld hl,BCALLRoutineStart ld de,appBackUpScreen ld bc,BCALLRoutineEnd-BCALLRoutineStart ldir ld a,7Ch call translatePage ld ix,_EraseFlashPage-4000h call appBackUpScreen ;Copy swap sector to sector 3Ch/7Ch call copySector ;---------------------------------- ;Erase swap sector and write 0FEh to 4000h ld a,(swapSectorPage) push af B_CALL EraseFlashPage pop af ld de,4000h ld b,0FEh B_CALL WriteAByte ;Display successful B_CALL RunIndicOff B_CALL newline ld hl,sSuccessful call PutSApp B_CALL GetKey jr StartApp uninstallIOHook: ;Are we even installed? call IsInstalled jr nc,$F notInstalled: B_CALL ClrLCDFull B_CALL HomeUp ld hl,sNotInstalled call PutSApp B_CALL GetKey jr StartApp $$: ;Display status B_CALL ClrLCDFull B_CALL HomeUp ld hl,sUninstalling call PutSApp ;Unlock Flash call UnlockFlash ;Erase swap sector B_CALL FindSwapSector ld (swapSectorPage),a B_CALL EraseFlashPage ;Find and store all instances of B_CALL RecAByteIO call FindRecAByteIO ;Look up and save patch addresses for above routines call GetVectorInfo ;Selectively copy page 3Ch/7Ch to swap sector except for 6-byte blocks ; for each routine call copyPartialPage ;Copy page 3Dh/7Dh to second page of swap sector ld a,(swapSectorPage) inc a ld b,a ld a,7Dh push bc call translatePage pop bc B_CALL CopyFlashPage ;Write original 6-byte blocks from end of page 70h to swap sector ld b,5 ld hl,7F00h $$: push bc ld a,70h call translatePage ld de,SendCByteBlock ld bc,6 push de B_CALL FlashToRam pop de ld ix,9 add ix,de push ix pop de pop bc call AnimateRunIndicator djnz $B call writeBlocks ;Write _GetByteOrBoot to all instances of _RecAByteIO ld hl,appData ld (hl),0EFh inc hl ld (hl),7Bh inc hl ld (hl),80h ld bc,(numMatches-1) ld a,b or a jr z,skipRecAByteIO ld c,0 $$: push bc ld h,0 ld l,c add hl,hl ld bc,matches add hl,bc ld e,(hl) inc hl ld d,(hl) ld bc,3 ld hl,appData ld a,(swapSectorPage) B_CALL WriteFlash pop bc inc c call AnimateRunIndicator djnz $B skipRecAByteIO: ;---------------------------------- ;The below must be done without OS ;Erase sector 3Ch/7Ch ld hl,BCALLRoutineStart ld de,appBackUpScreen ld bc,BCALLRoutineEnd-BCALLRoutineStart ldir ld a,7Ch call translatePage ld ix,_EraseFlashPage-4000h call appBackUpScreen ;Copy swap sector to sector 3Ch/7Ch call copySector ;---------------------------------- ;Erase swap sector and write 0FEh to 4000h ld a,(swapSectorPage) push af B_CALL EraseFlashPage pop af ld de,4000h ld b,0FEh B_CALL WriteAByte ;Wipe out our code ld a,70h call translatePage B_CALL EraseFlashPage ;Display successful B_CALL RunIndicOff B_CALL newline ld hl,sSuccessful call PutSApp B_CALL GetKey jr StartApp GetVectorInfo: ld hl,GetVectorInfoStart ld de,appBackUpScreen ld bc,GetVectorInfoEnd-GetVectorInfoStart ldir jp appBackUpScreen GetVectorInfoStart: in a,(6) push af ld a,7Bh call vectorTranslatePage-GetVectorInfoStart+appBackUpScreen out (6),a ld hl,_SendCByte ld de,SendCByteAddress call GetVectorHLInfo-GetVectorInfoStart+appBackUpScreen ld hl,_ContinueGetByte call GetVectorHLInfo-GetVectorInfoStart+appBackUpScreen ;It's stuff like this that makes me doubt the viability of this... ;This is so wrong, but it works (for now) push de ld hl,-6 add hl,de ld a,(hl) cp 0FDh ld hl,(ContinueGetByteAddress) inc hl inc hl inc hl jr nz,$F inc hl $$: ld (ContinueGetByteAddress),hl pop de ld hl,_Rec1stByteNC call GetVectorHLInfo-GetVectorInfoStart+appBackUpScreen ld hl,_RecAByteIO call GetVectorHLInfo-GetVectorInfoStart+appBackUpScreen ld hl,_Send4Bytes call GetVectorHLInfo-GetVectorInfoStart+appBackUpScreen pop af out (6),a ret GetVectorHLInfo: push hl ld bc,3 ldir pop ix ld l,(ix+0) ld h,(ix+1) ld a,(ix+2) ld bc,6 B_CALL FlashToRam ret vectorTranslatePage: 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 GetVectorInfoEnd: FindGetByteOrBoot: ld hl,findPatternStart ld de,appBackUpScreen ld bc,findPatternEnd-findPatternStart ldir xor a ld (numMatches),a ld de,4000h $$: ld a,7Ch call translatePage ld ix,searchPattern3-findPatternStart+appBackUpScreen call appBackUpScreen ret nz ld de,(saveSScreen) ld hl,(numMatches) ld h,0 add hl,hl ld bc,matches add hl,bc ld (hl),e inc hl ld (hl),d inc de ld hl,numMatches inc (hl) call AnimateRunIndicator jr $B FindRecAByteIO: ld hl,findPatternStart ld de,appBackUpScreen ld bc,findPatternEnd-findPatternStart ldir xor a ld (numMatches),a ld de,4000h $$: ld a,7Ch call translatePage ld ix,searchPattern4-findPatternStart+appBackUpScreen call appBackUpScreen ret nz ld de,(saveSScreen) ld hl,(numMatches) ld h,0 add hl,hl ld bc,matches add hl,bc ld (hl),e inc hl ld (hl),d inc de ld hl,numMatches inc (hl) call AnimateRunIndicator jr $B PatchSector70hCode: ld hl,findPatternStart ld de,appBackUpScreen ld bc,findPatternEnd-findPatternStart ldir $$: ld a,7Dh ld de,4000h call translatePage ld ix,searchPattern1-findPatternStart+appBackUpScreen call appBackUpScreen jr nz,$F ld hl,appData push hl ld bc,5 B_CALL MemClear pop hl ld a,(iMathPtr4) ld de,(saveSScreen) ld bc,5 B_CALL WriteFlash call AnimateRunIndicator jr $B $$: ld a,7Dh call translatePage ld ix,searchPattern2-findPatternStart+appBackUpScreen ld de,4000h call appBackUpScreen ret nz ld hl,appData push hl ld bc,9 B_CALL MemClear pop hl ld a,(iMathPtr4) ld de,(saveSScreen) ld bc,9 B_CALL WriteFlash call AnimateRunIndicator jr $B findPatternStart: ;Find pattern in IX, Flash page in A, starting address in DE ;Returns NZ if pattern not found ;(savesscreen) contains the address of match found ;Search pattern: terminated by 00h ; 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 (saveSScreen),de push ix pop hl searchLoop: ld b,(hl) ld a,b or a ret z inc de inc a jr z,$F dec de ld a,(de) inc de bit 7,d ret nz cp b jr z,$F ld de,(saveSScreen) jr searchLoopRestart $$: inc hl jr searchLoop findPatternRet: pop bc ld a,b out (6),a ret searchPattern1: DB 3Eh,70h,0EFh,84h,80h,0 searchPattern2: ld c,70h ld b,4 DB 0CDh,0 searchPattern3: DB 0EFh,7B,80h,0 searchPattern4: DB 0EFh,03h,4Fh,0 findPatternEnd: BCALLRoutineStart: ;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-BCALLRoutineStart+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-BCALLRoutineStart+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: copySector: ld a,(swapSectorPage) ld (srcPage),a ld a,7Ch ld (destPage),a ld b,2 copySectorLoop: push bc ld hl,4000h ld bc,4000h $$: push bc ld a,(srcPage) call translatePage ld ix,_LoadAIndPaged-4000h call appBackUpScreen ld b,a push bc ld a,(destPage) call translatePage pop bc ex de,hl push de ld ix,_WriteAByte-4000h call appBackUpScreen pop hl inc hl pop bc dec bc ld a,b or c jr nz,$B call AnimateRunIndicator pop bc ld hl,srcPage inc (hl) inc hl inc (hl) djnz copySectorLoop ret copyPartialPage: ;Copy page 7Ch to (swapSectorPage) unless the 6-byte block is one of our routines ld hl,4000h $$: ld a,7Ch call translatePage B_CALL LoadAIndPaged ld b,a ;Is this a special address? push bc call IsAtSpecialBlock pop bc jr z,isSpecialBlock ld a,(swapSectorPage) ex de,hl B_CALL WriteAByte ex de,hl jr copyPartialContinue isSpecialBlock: ld bc,6 add hl,bc copyPartialContinue: bit 7,h ret nz jr $B IsAtSpecialBlock: ld bc,(numMatches-1) ld ix,matches $$: push bc ld c,(ix+0) ld b,(ix+1) call cphlbc pop bc ret z djnz $B ld bc,(SendCByteAddress) call cphlbc ret z ld bc,(ContinueGetByteAddress) call cphlbc ret z ld bc,(Rec1stByteNCAddress) call cphlbc ret z ld bc,(RecAByteIOAddress) call cphlbc ret z ld bc,(Send4BytesAddress) cphlbc: push hl or a sbc hl,bc pop hl ret writeBlocks: ;Write 6-byte blocks to swap sector ld a,(swapSectorPage) ld de,(SendCByteAddress) ld hl,SendCByteBlock ld bc,6 call BlockWriteFlash ld de,(ContinueGetByteAddress) ld hl,ContinueGetByteBlock call BlockWriteFlash ld de,(Rec1stByteNCAddress) ld hl,Rec1stByteNCBlock call BlockWriteFlash ld de,(RecAByteIOAddress) ld hl,RecAByteIOBlock call BlockWriteFlash ld de,(Send4BytesAddress) ld hl,Send4BytesBlock BlockWriteFlash: push af push bc B_CALL WriteFlash pop bc pop af ret copyMyself: $$: push bc push hl ld a,70h call translatePage ld b,(hl) B_CALL WriteAByte pop hl inc hl pop bc dec bc ld a,b or c jr nz,$B ret