;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; PROGRAM INFORMATION ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; Name : Where Are My Progs ? v1.1 (CrASH only version) ; Author : Wouter Demuynck (kadem@unicall.be) ; Calculator : TI-82 ; Shells (tested) : Crash 1.1, Crash 1.2, Crash 1.3 ; ROM versions (tested) : 19.0 ; Program size (on calc) : 547 bytes ; Release Date : 19/01/98 ; CrASH interrupt ID = $81 ; last update : 19/01/98 18:09 ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; EXTRA INFORMATION ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; Thanks to Ilya Winham (ilyamojo@geocities.com) for this information ; (I found this info on his TI-82 bugs page) : ; ; | Visible in | ; Programs Starting with | PRMG EXEC | PRGM EDIT | DELETE | ; ------------------------------+-----------+------------+---------+ ; !, @, #, $, %, &, -,(, ) | NO | NO | NO | ; `, ~, _, ^, {, } | YES | ? | ? | ; other characters | YES | YES | YES | ; ; Programs with symbols after the first character can be seen in both menus. ; ; When you try to execute a program with a symbol you get a syntax error. ; If you try to delete the programs your calculator might lock up! ; ; Some usefull documents I used : ; - 82-var.txt : information about the VAT ; - 82hack.txt : a little information about the VAT ; - 82-ram.txt : usefull RAM locations (e.g. pointer to end of VAT) ; - Crash2CrASH v1.3 : Another Crash Interrupt Routine (look at the source) ; - CrashPrg.txt : Information on programming interrupts in Crash ; (this file is included in the CrASH package) ; - Some usefull info on Interrupts by Jimmy Mardell. (e-mail sent to ; the TI-CALC mailing list/ check out the archives or mail me for a ; copy (kadem@unicall.be) ; ; All these files can be found on the http://www.ticalc.org website ; or the http://ti-files.home.ml.org website ! ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; LOAD CRASH ALIASES AND TITLE ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ .INCLUDE CRASH82.INC .DB "NoProgs v1.1 - " State: .DB "Disabled" .DB 0 ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; SOME ALIASES FOR EASY CODE WRITING/READING ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ;alias | addr | description PROGSTART = $8282 ; address where the int prog will be copied to DATA = $834A ; address where the data will be saved VECTOR = $8400 ; address where the vector table will be set up LASTVAT = $8D18 ; pointer to last entry of vat ONLYONCE = $8527 ; a byte at the end of the APD_BUF, used to check if the ; routine has just been looped through or not ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; CODE TO INSTALL AND DESINSTALL THE INTERRUPT CODE ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ CALL CR_GRBCopy ; clear the LCD (needed for CrASH 1.2/1.3) ; (GRAPH_MEM is cleared on startup) LD HL, $0000 LD (CURSOR_POS), HL ; -> row 0 col 0 set 3,(IY+05) LD HL, text ; Display text (inversed) ROM_CALL(D_ZT_STR) res 3,(IY+05) LD BC, $0004 LD (CURSOR_POS), BC ; -> row 4 col 0 ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Check what we have to do ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ LD A, (INT_STATE) ; INT_STATE contains info on loaded interrupts CP $81 JR Z, Remove ; if our interrupt is loaded, remove it OR A ; CP 0 JR NZ, Another ; if another interrupt is loaded, show a message ; else, install our interrupt ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Install the interrupt ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ROM_CALL(D_ZT_STR) ; Display 'installed' message ; there is no need to do ; ld hl,installed ; since hl already points there ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Setup interrupt vector table in later half of ;; APD_BUF. The interrupt routine will be placed ;; from $8282 . The wasted space between the end ;; of the program and the table at $8400 will be ;; used to save data (that's $8282+200 -> $83FF) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LD HL, $8400 LD DE, $8401 LD (HL), $82 LD BC, $100 LDIR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; copy interrupt routine to $8282 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LD HL, IntProcStart ; Get pointer to interrupt routine LD DE, PROGSTART ; Start of int routine LD BC, IntProcEnd-IntProcStart+1 ; calculate lenght of routine LDIR ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Set the first DATA byte and ONLYONCE to 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; XOR A ; LD A,0 LD (DATA),A LD (ONLYONCE),A ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Write Nr. Of free bytes in APD_BUF to APD_FREE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LD HL, 90 ; there are only 90 bytes at the beginning LD (APD_FREE), HL ; of APD_BUF available for other programs... ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Update Title To Enabled ;;;;;;;;;;;;;;;;;;;;;;;;;; ld hl,MsgEnabled ld de,State ld bc,8 ldir ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Now that everything is set up, set the interrupt mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LD A, $84 ; Point to the new table ($8400) LD I, A IM 2 ; interrupt mode 2 ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Wait for key and return to CrASH ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ WaitKey: ; After this, it will return to CrASH JP CR_KHAND ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Remove the interrupt routine ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Remove: LD HL, deinstalled ; Display 'disabled' message ROM_CALL(D_ZT_STR) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; put the interrupt mode back to normal ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; IM 1 ; Normal interrupt mode ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Change Title To Disabled ;;;;;;;;;;;;;;;;;;;;;;;;;;; ld hl,MsgDisabled ld de,State ld bc,8 ldir ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Reset the CrASH variables back to default ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LD HL, $300 ; $300 = 768 bytes free LD (APD_FREE), HL XOR A ; Int state is 0 off LD I, A ; This needs to be done ; I = Interrupt page address register (8-bit) LD (INT_STATE), A ; No interrupts loaded (0) JR WaitKey ; go and wait for a key ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Display the 'another interrupt loaded' message ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Another: LD HL, another ROM_CALL(D_ZT_STR) JR WaitKey ; go and wait for a key ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; Program Dialogs Data ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ text: .DB " Where are my " .DB " progs ? v1.1 ",0 installed: .DB " Enabled. " .DB "LEFT/RIGHT keys " .DB " together", 0 deinstalled: .DB " Disabled.", 0 another: .DB "Please Turn Of " .DB "Other Running " .DB "Handlers Before " .DB "Installing", 0 MsgDisabled: .DB "Disabled" MsgEnabled: .DB "Enabled " ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ ; START OF THE ACTUAL INTERRUPT PROGRAM ;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ IntProcStart: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Disable interrupts and exchange all registers with the shadow regs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DI EX AF, AF' EXX ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Change some addresses and check if we have to run the program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LD A, $81 ; Int state is 81 - on (ID) LD (INT_STATE), A ; (to tell other programs our interrupt is loaded) LD A, ($800A) ; Word 8009: Counter used for APD (MSB in 8009) CP $11 JR C, IntOff ; If APD counter < 17, call standard off (2nd+off) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Check if we are on the homescreen ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LD A, ($8121) ; Code for which "function" of the calc is used CP $40 ; $40 is the code for the homescreen JR NZ,SomeLabelBetween ; if not in home screen, get out of interrupt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Check the state of the busy counter ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BIT 0, (IY+$12) JR NZ,SomeLabelBetween ; If busy indicator on, get out of interrupt ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Check if we are in a menu ;;;;;;;;;;;;;;;;;;;;;;;;;;;; BIT 4, (IY+$0C) JR NZ,SomeLabelBetween ; If in menu, get out of interrupt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Check if the right keys are pressed ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LD A, $FE ; Check for left and right OUT ($01), A IN A, ($01) BIT 1, A JR NZ,SomeLabelBetween ; left key not pressed -> get out BIT 2, A JR NZ,SomeLabelBetween ; right key not pressed -> get out ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Still need to check if we may or may not do the loop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LD A,(ONLYONCE) OR a ; CP 0 JR NZ,SomeLabelBetween2 ; if we already did our work, quit the interrupt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; OK ! We can run the interrupt routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ld hl,VAT_START ; pointer to the start of the VAT ld d,0 ; will be changed to 1 if we are at the last vat-entry jr Loop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Do A Standard TI-OFF When You Leave ;; The Calc On For A While (A Real APD ;; Would Crash The Calc !) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; IntOff: LD HL, $058C ; Store TI-OFF routine (when 2nd+off is pressed) EX (SP), HL JR ExitInt ; had to add this, otherwise, it wasn't possible to get all jumps relative SomeLabelBetween: xor a ; change ONLYONCE to 0 if LEFT and RIGHT keys LD (ONLYONCE),A ; are not pressed or if not in homescreen SomeLabelBetween2: jr ExitInt ; exit the int routine Loop: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Check if we are at the last VAT entry ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; push de ; store 'DE' value (we need to remember 'D' ) ex de,hl ld hl,(LASTVAT) ; hl contains start address of the last VAT entry call CP_HL_DE ; compare HL and DE ex de,hl pop de ; get original 'DE' value back jr nz,NotLastEntry ld d,1 ; change 'D' to 1 if we reached the last entry NotLastEntry: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Get Type Of Entry and check if it is a program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ld a,(hl) ; read type of VAT-entry and %00001111 ; mask out 4 MSB (they aren't always zero) cp $05 ; program jr z,ItISaProgram cp $06 ; protected program jr nz,ItsNOTaProgram ;;;;;;;;;;;;;;;;;;;;;;;; ;; It is a program entry ;;;;;;;;;;;;;;;;;;;;;;;; ItISaProgram: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Move on in vat untill we have the address with the name lenght ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; dec hl ; -> addr 1 dec hl ; -> addr 2 dec hl ; -> length of name ld a,(hl) ; load length in 'A' dec a ld b,a ; b will be used for a loop later dec hl ; -> first char of name ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Load first char of name and check if we need to hide or reveal ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ld a,(hl) cp '(' ; hidden programs have a name starting with '(' jr nz,Hide ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Read First Char From DATA buffer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ld a,(DATA) ; DATA = $834A = $8282+200 ; move everything in buffer 1 byte to the left push bc ; 'B' contains length of name - 1 push de ; 'D' contains 1 if we reached last entry push hl ; needs to be safed ('HL' points somewhere in the VAT) LD HL, DATA + 1 ; Start of where to read ($8282 + 201) LD DE, DATA ; Start of where to write ($8282 + 200) LD BC, 180 ; Number of bytes to copy ($83FF - $834B) LDIR ; do the thing ('HL' and 'DE' are increased) pop hl pop de pop bc jr WriteInVAT ;;;;;;;;;;;;;;;;;;; ;; Hide The Program ;;;;;;;;;;;;;;;;;;; Hide: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; If The Program Name starts with '!' or '#', don't hide it ;; (there are 2 system programs called '!' and '#'. The best ;; thing is not to change them I guess) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cp '!' jr z,ProgramName cp '#' jr z,ProgramName ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Write first char of name at the end of the DATA buffer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; push hl ld a,(hl) push af ld hl,DATA ; DATA = $834A = $8282 + 200) HideLoop: xor a ; ld a,(hl) cp (hl) ; cp 0 jr z,HideFoundIt inc hl jr HideLoop HideFoundIt: pop af ld (hl),a xor a inc hl ld (hl),a pop hl ld a,'(' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Write the new data to the VAT ;; this is a '(' when we hide a program ;; or the first char if we are un-hiding it ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; WriteInVAT: ld (hl),a ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Change HL untill it points to the start of the next VAT entry ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ProgramName: xor a cp b ; check if b =0 jr z,DoneProgramName ; if so, don't do the loop dec hl djnz ProgramName DoneProgramName: dec hl ; next var jr Pause ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; It is not a Program Entry ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ItsNOTaProgram: ; if the entry is not a program entry dec hl ; -> addr 1 dec hl ; -> addr 2 dec hl ; -> token 1 dec hl ; -> token 2 dec hl ; -> zero byte (at least, mostly zero) dec hl ; -> next entry ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Check if we need to loop again ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Pause: ld a,d cp 1 ; if 'D' doesn't contains 1, keep looping jr nz,Loop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Change a byte on the TEXT_MEM to prevent ;; interrupt from running again in twice a ;; second ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ld hl,ONLYONCE ; set ONLYONCE to 1 inc (hl) ; (will be reset to 0 when LEFT/RIGHT not pressed) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Get registers back from the shadow registers ;; and call standard interrupt handler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ExitInt: EXX EX AF, AF' JP $0038 ; Call Std interrupt handler, go back. IntProcEnd: