;----------------------------------------------------------- ; ; Api92/Api89 Source Code ; ; (c) NeXO Software, Benoit SCHERRER ; e-mail : p.c.scherrer@wanadoo.fr ; ; ; File> utils.xas ; Utility> General utility functions ; ;----------------------------------------------------------- ;--------------------------------------------------------- ; Lock a handle, checking if it has already been locked. ; MUST USE api92::HeapUnlock to unlock the handle ; ;Input> d0 : handle to lock ;Out> d1.w = 0 if ok, else handle already locked ; (save d0 to know whether unlock the handle) ;--------------------------------------------------------- apilib@007B: _util_HeapLock: movem.l d0/d2/a0-a1,-(a7) move.w d0,-(a7) jsr tios::HeapGetLock moveq.w #1,d1 tst.w d0 bne.s \exit move.w (a7),d0 jsr tios::HeapLock clr.w d1 \exit addq.l #2,a7 movem.l (a7)+,d0/d2/a0-a1 rts ;--------------------------------------------------------- ; Hardware Verion of the calculator ;Input> / ;Out> d0.w : HW Version ;--------------------------------------------------------- apilib@0012: _util_GetHWVersion ;#ifdef TIPLUS movem.l d1/a0-a1,-(a7) move.l ($C8),d1 and.l #$600000,d1 move.l d1,a1 move.l $104(a1),a0 move.l a0,d0 sub.l d1,d0 moveq.l #1,d1 cmp.l #$FFFF,d0 bhi.s \exit cmp.w #22,(a0) bls.s \exit move.w 24(a0),d1 \exit move.w d1,d0 movem.l (a7)+,d1/a0-a1 ;#else moveq.l #1,d0 ;#endif rts ;--- previous version ------------- ; movem.l d7/a0,-(a7) ; move.l $C8,d0 ; and.l #$600000,d0 ; move.l d0,a0 ; move.l $104(a0),a0 ; moveq.l #1,d7 ; sub.l a0,d0 ; pour VTI ; cmp.l #-$10000,d0 ; FL_getHardwareParmBlock ; bls.b \exit ; le fait aussi ; cmp.w #$16,(a0) ; bls.b \exit ; move.l $16(a0),d7 ; ;\exit move.l d7,d0 ; movem.l (a7)+,d7/a0 ;---------------------------------- ;----------------------------------------------------------- ; Get all required libs by a Kernel program ; ;Input> d0.w : Handle of program/Lib ; d2.w : Handle of array where to put items. ; (Created by array_Create, with size of ; each element = 14) ;Out> d1.w : Error code ;----------------------------------------------------------- apilib@007A: _util_GetRequiredLibraries: movem.l d0/d2-d5/a0-a2,-(a7) move.w d2,d4 moveq.w #EC_UNKNOWN,d1 tst.w d0 beq \ex tst.w d2 beq \ex ;---------------------------------- ;Lock program ;---------------------------------- ;#ifdef TIPLUS bsr _util_HeapLock ;try to lock d0 =prog/lib move.w d0,-(a7) ;save handle for unlock move.w d1,-(a7) ;save d1 = already locked or not ;#endif bsr DEREFd0a0 ;Get handle address ;---------------------------------- ;Check if EXE/PLUG/LIB ;---------------------------------- moveq.w #EC_INVALIDFILE,d1 ;error code bsr _GetFileExt ;get file extension from a0 cmp.w #EXT_EXE,d2 beq.s \skip cmp.w #EXT_PLUG,d2 beq.s \skip cmp.w #EXT_LIB,d2 bne.s \exit \skip clr.w d1 ;no error ;---------------------------------- ; Goto import lib table ;---------------------------------- ;#ifdef TIPLUS adda.w #$1C,a0 ;$1A +2 ;#else move.l a0,a2 adda.w $12(a2),a0 ;#endif move.w (a0)+,d5 ;Number of libraries beq.s \exit ;if null exit subq.w #1,d5 ;for dbra ;#ifdef TIPLUS move.l a0,a2 ;#else adda.w (a0),a2 ;Addr of first lib ;#endif \loop ;---------------------------------- ;Look if lib name already added ;usefull because of recursive get lib ;---------------------------------- move.w d4,d0 ; moveq.w #VAT_ENTRYSIZE,d1 ;size of items moveq.w #8,d3 ;compare with only 8 first bytes bsr copy_in_buf ;-> in a0 bsr _array_FindItem ;(a0) tst.w d0 bne.s \next ;---------------------------------- ;If not, add it ;---------------------------------- move.w d4,d0 moveq.w #VAT_ENTRYSIZE,d1 ;item size bsr _array_AddItem ;add (a0) tst.w d1 bne.s \memerror ;---------------------------------- ; look for it in the VAT and get ; required libs by this lib ;---------------------------------- move.l a1,-(a7) bsr _vat_FindVarEverywhere move.l (a7)+,a1 tst.w d2 beq.s \next move.w d1,VAT_ENTRYHDL(a1) ;put handle if found move.w d1,d0 move.w d4,d2 bsr _util_GetRequiredLibraries tst.w d1 ;if error bne.s \exit ;---------------------------------- ;Next lib... ;---------------------------------- \next ;#ifdef TIPLUS adda.w #10,a2 ;#else \ni tst.b (a2)+ bne.s \ni ;#endif dbra.s d5,\loop \exit ;---------------------------------- ; Unlock program/lib ;---------------------------------- ;#ifdef TIPLUS tst.w (a7)+ ;was handle already locked? bne.s \nounlock ;if yes skip move.w d1,d5 jsr tios::HeapUnlock ;else unlock handle move.w d5,d1 \nounlock addq.l #2,a7 ;#endif \ex movem.l (a7)+,d0/d2-d5/a0-a2 rts \memerror moveq.w #EC_OUTOFMEMORY,d1 bra.s \exit ; a2 => a0 copy_in_buf: movem.l a1-a3,-(a7) lea _buf(PC),a3 clr.l (a3) clr.l 4(a3) clr.l 8(a3) clr.l 12(a3) move.l a3,a0 \copy move.b (a2)+,(a3)+ bne.s \copy movem.l (a7)+,a1-a3 rts _buf dc.l 0,0,0,0 ;----------------------------------------------------- ; WaitKey ; ; > The function wait for a key pressed by the user ; of the program ; ;Input> nothing ;Out> d0.w : Keycode of pressed key ; No Other Register Modified ;----------------------------------------------------- _WaitKey: apilib@002B: movem.l a0-a6/d1-d7,-(a7) ;#ifdef TIPLUS \APD_reset move.w #2,-(a7) TIOS_CALL OSTimerRestart ; resets APD timer addq.l #2,a7 \keyloop move.w #2,-(a7) TIOS_CALL OSTimerExpired ; if APD expired.. addq.l #2,a7 tst.w d0 beq \noshutdown \shutdown trap #4 ; then shut down bra \APD_reset \noshutdown ;#ifdef NSTUB TIOS_CALL kbhit tst.w d0 beq.s \keyloop ;#else tst.w KEY_PRESSED_FLAG ; has a key been pressed ? beq \keyloop ; no.. ;#endif TIOS_CALL ngetchx ; yes : get the key code cmp.w #KEY_DIAMOND+$10B,d0 ; Diamand+ON shuts down the calc beq.s \shutdown cmp.w #$1000+$10B,d0 ; 2nd+ON shuts down the calc beq.s \shutdown move.w d0,d6 move.w #3,-(a7) TIOS_CALL ST_busy ; clears BUSY flag addq.l #2,a7 move.w d6,d0 ;#else \idle_start: move.l APD_INIT,APD_TIMER clr.w APD_FLAG \WaitKey tst.w APD_FLAG bne.s \off tst.w tios::kb_vars+$1C beq.s \WaitKey move.l APD_INIT,APD_TIMER ; reset APD timer (1) clr.w tios::kb_vars+$1C move.w tios::kb_vars+$1E,d0 cmp.w #$210B,d0 bne.s \no_off \off trap #4 bra.s \idle_start \no_off ;#endif movem.l (a7)+,a0-a6/d1-d7 rts ;------------------------------------------------------ ; util_GetAPD ; ; > Get the current APD value. Notice that on a ; normal calculator, you can divide by 20 to ; convert the returned tick value in seconds ; ;Input> Nothing ;Out> d0.l : ticks value ; No Other Register Modified ;------------------------------------------------------ apilib@005F: util_GetAPD: ;#ifdef TIPLUS movem.l d1-d7/a0-a6,-(a7) move.w #$700,d0 ;disable interrupts trap #1 move.l d0,-(a7) move.w #2,-(a7) ;restart timer 2 TIOS_CALL OSTimerRestart move.w #2,(a7) ;and gets its value TIOS_CALL OSTimerCurVal addq.l #2,a7 move.l d0,d1 ;save d0 move.l (a7)+,d0 ;restore SR move.l d1,-(a7) trap #1 move.l (a7)+,d0 movem.l (a7)+,d1-d7/a0-a6 ;#else move.l APD_INIT,d0 ;#endif rts ;------------------------------------------------------ ; util_SetAPD ; ; > Set the APD value. Notice that on a ; normal calculator, you can multiply by 20 to ; convert seconds in tick value ; ;Input> d0.l : ticks value ;Out> No Register Modified ;------------------------------------------------------ apilib@0060: util_SetAPD: ;#ifdef TIPLUS movem.l d0-d7/a0-a6,-(a7) move.l d0,d3 ;save d3 move.w #2,-(a7) ;timer number 2 TIOS_CALL OSFreeTimer move.l d3,-(a7) ;ticks move.w #2,-(a7) ;timer nb 2 TIOS_CALL OSRegisterTimer ;set it addq.l #8,a7 movem.l (a7)+,d0-d7/a0-a6 ;#else move.l d0,APD_INIT ;#endif rts ;------------------------------------------------------ ; DecimalToStr ; ; > This function convert using decimal base a number ; to a null-terminated string ; ;Input> d0.l : The number to convert ; a0.l : Address of END of thestring buffer ; ( must be large enough ) ;Out> a0.l : address of the beginning of the string ; No Other Register Modified ;------------------------------------------------------ apilib@000C: movem.l d0/d2,-(a7) \loop divu #10,d0 move.l d0,d2 swap d2 add.b #48,d2 move.b d2,-(a0) and.l #$FFFF,d0 bne.s \loop movem.l (a7)+,d0/d2 rts ;------------------------------------------------------ ; HexaToStr ; ; This function convert using hexadecimal base a number ; to a null-terminated string ; ;Input> d0.l : The number to convert ; d1.w : Number of digit-1 ; a0.l : Address of string buffer ( must be large ; enough ) ;Out> No Register Modified ;------------------------------------------------------ apilib@000D: HexaToStr: movem.l d0-d3/a0,-(a7) \loop move.l d0,d3 ; save the number move.l d1,d2 ; d2=Nb of charac-1 lsl.l #2,d2 ; d2 = 4*loopcount (# bits to shift) lsr.l d2,d0 and.l #$0000000F,d0 cmp.l #9,d0 ;d0=9? bhi.s \letter add.w #48,d0 ;d0=48 bra.s \char \letter add.w #55,d0 ;d0=d0+55 \char move.b d0,(a0)+ move.l d3,d0 ; restore the saved number dbra.s d1,\loop clr.b (a0)+ ;Put the end zero movem.l (a7)+,d0-d3/a0 rts ;----------------------------------------------------- ;input> a0.l : source string ; a1.l : destination string ; d0.w : Max number of chars ;----------------------------------------------------- CopyStr: apilib@001B: movem.l d0-d1/a0-a1,-(a7) clr.w d1 ;Number of copied chars \cpy move.b (a0)+,(a1)+ ;Copy char addq.w #1,d1 ;increment char count cmp.w d0,d1 ;if max beq.s \toobig ;then go at \toobig tst.b (a0) ;if end of string exit loop beq.s \skip cmp.b #$0D,(a0) bne.s \cpy bra.s \skip \toobig subq.l #3,a1 ;if too big move.b #'.',(a1)+ ;put '...' move.b #'.',(a1)+ move.b #'.',(a1)+ \skip clr.b (a1)+ ;and put 0x00 at the end of the string movem.l (a7)+,d0-d1/a0-a1 rts ;------------------------------------------------------ ; GetFreeRAM ; ; > Get the available RAM ; ;Input> Nothing ;Out> d0.l : Free RAM size ; No Other Register Modified ;------------------------------------------------------ apilib@000E: movem.l d1-d3/a0-a2,-(a7) ;#ifdef TIPLUS ; TIOS_CALL HeapCompress TIOS_CALL HeapAvail ;#else ;#ifdef TIPLUS ; move.l #tios::MaxHandles,a1 ;max hdl ; move.w (a1),d2 ;max hdl ; move.l $16(a1),a0 ;MaxHdl+$16=Heap ; addq.l #4,a0 ; subq.w #2,d2 ;#else move.l #tios::globals,a1 move.l $1902(a1),a0 ;heap table move.w $18EC(a1),d2 ;max element before resize subq.w #1,d2 ;#endif clr.l d1 clr.l d3 \loop tst.l (a0) beq.s \skip move.l (a0),a2 ;#ifdef TIPLUS ; cmp.l #$200000,a2 ;if TI92+ ; bge.s \skip ;avoid Archives ;#endif move.w -(a2),d3 bclr.l #$0F,d3 add.l d3,d1 \skip addq.l #$4,a0 dbra.s d2,\loop lsl.l #1,d1 ;#ifdef TIPLUS ; move.l tios::TopHeap,d0 ;#else move.l $18F6(a1),d0 ;top of memory heap ;#endif sub.l d1,d0 sub.l a0,d0 ;d0 is the free mem ;#endif movem.l (a7)+,d1-d3/a0-a2 rts ;------------------------------------------------------ ; d0.w : Free archive ; d1.w : used archive ;------------------------------------------------------ _GetFreeArchive: apilib@0071: ;#ifdef TIPLUS movem.l d2/a0-a1/a6,-(a7) suba.w #4*3,a7 move.l a7,a6 clr.l -(a7) clr.l -(a7) clr.l -(a7) move.l a6,-(a7) pea 4(a6) pea 8(a6) TIOS_CALL EM_survey lea 24(a7),a7 move.l (a6),d0 add.l 4(a6),d0 move.l 8(a6),d1 adda.w #4*3,a7 movem.l (a7)+,d2/a0-a1/a6 ;#else clr.l d0 clr.l d1 ;#endif rts ;------------------------------------------------------ ; DEREFd0a0 ; ; This function is used for size optimization : with only ; one library call, it returns the address of a handle, ; modifing only a0 ; ;Input> d0.w : The handle ;Out> a0.l : The address of the handle in d0 ; No Other Register Modified ;------------------------------------------------------ apilib@000F: DEREFd0a0: ;#ifdef TIPLUS lsl.w #2,d0 move.l tios::Heap,a0 move.l 0(a0,d0.w),a0 ;#else tios::DEREF d0,a0 ;#endif lsr.w #2,d0 ;restore hdl rts ;---------------------------------------------------- ; SetIntVector ; ; Set an interrupt vector ; This function can be used both to set a new ; interrupt vector and to restore it. ; You MUST ALWAYS save the address of the previous ; routine given in a1. ; ;input> a0 : vector address to set ; a1 : address of the new routine ; ;out> a1 : addr of previous routine ; No Other Register Modified ;---------------------------------------------------- apilib@0010: movem.l d0-d1/a0,-(a7) move.w #$0700,d0 ;dis interrupt trap #1 move.l (a0),d1 bclr.b #2,$600001 move.l a1,(a0) bset.b #2,$600001 trap #1 move.l d1,a1 movem.l (a7)+,d0-d1/a0 rts ;----------------------------------------------------- ; DecodeEXPRVar ONLY TI-92+ ; ; This function will return in an handle the decoded ; contents of a EXPR variable. ; ;Input> d0.w : handle of EXPR variable ;Out> d0.w : Handle of creater buffer ; a1.l : address of buffer where decoded datas ; are. ; ; You MUST delete the created buffer after its ; use. ;----------------------------------------------------- apilib@0019: DecodeEXPRVar: ;#ifdef TIPLUS movem.l d1-d7/a0/a2-a6,-(a7) move.l $C8,a4 ;get ROM functions table addr move.w d0,-(a7) ;Push handle of the file move.l 154*4(a4),a5 ;154th function of the table jsr (a5) ;Call ROM function addq.l #2,a7 ;Pop the stack move.w 2(a1),d0 ;get size of file ext.l d0 ;make d0 a long-word lea 1+2(a1,d0.l),a1 ;address of end of the file move.l a1,-(a7) ;copy of the $4CCE36 function move.l a1,-(a7) move.l 266*4(a4),a5 ;266th function of the table jsr (a5) ;Call ROM function addq.l #4,a7 move.l a0,-(a7) move.l 744*4(a4),a5 ;154th function of the table jsr (a5) ;Call ROM function addq.l #8,a7 move.w #0,-(a7) ;? move.w #1,-(a7) ;? move.l $76FC,-(a7) ; move.l 79*4(a4),a5 ;79th function of the table jsr (a5) ;call function addq.l #8,a7 addq.l #2,a1 movem.l (a7)+,d1-d7/a0/a2-a6 ;#endif rts ;------------------------------------------------- ;BatteriesLevel ; Input : void ; Output : d0.w = batteries level ; 4 - full battery state ; 3 - medium battery state ; 2 - low battery state ; 1 - BATT symbol appears (very low) ; 0 - extrem ;------------------------------------------------- _util_BatteriesState: apilib@0070: movem.l d2/d6/a0,-(a7) bsr _util_GetHWVersion move.w d0,d6 move.w #$700,d0 trap #1 move.l d0,-(a7) moveq.l #3,d2 ; for: 0x0000->0x0080->0x0100->0x0180 */ ;#ifdef TIPLUS cmp.b #1,d6 beq __h1_1 ;#else bra __h1_1 ;#endif movea.l #$70001D,a0 move.b (a0),d0 ; turn battery checker on -> bits 11 */ or.b #9,d0 move.b d0,(a0) __h1_1: movea.l #$600018,a0 ; set a0 to trigger level set register */ __bxp1: move.w #$380,(a0) ; set lowerest trigger level !!! */ moveq.l #$52,d0 ; how long should I loop for testing? */ __bxp2: btst.l #2,($600000) ; check if voltage is above triggerlevel */ dbne d0,__bxp2 ; not only once, but loop for a while ... */ moveq.l #3,d0 sub.w d2,d0 ; d0 = 0x0000->0x0080->0x0100->0x0180 */ lsl.w #7,d0 ; shift it up to trigger bits */ move.w d0,(a0) ; set new trigger level */ moveq.l #$6E,d0 ; ... and loop for a while testing it ... */ __bxp3: btst.l #2,($600000) dbra d0,__bxp3 bne.s __bxx ; voltage above ? exit ... */ dbf d2,__bxp1 ; more level to test? proceed ... */ __bxx: addq.b #1,d2 ; 1 <= d2.w <= 4 */ move.w #$380,(a0) ; set lowerest trigger level again */ ;#ifdef TIPLUS cmp.b #1,d6 beq __h1_2 ;#else bra __h1_2 ;#endif movea.l #$70001D,a0 ; set a0 to battery checker on register */ move.b (a0),d0 ; clear bit 3 */ and.b #$F7,d0 move.b d0,(a0) moveq.l #$52,d0 ; how long should I loop for testing? */ __bxx2: btst.l #2,($600000) ; check if voltage is above triggerlevel */ dbne d0,__bxp2 ; not only once, but loop for a while ... */ move.b (a0),d0 ; clear bit 0,too */ and.b #$F6,d0 move.b d0,(a0) __h1_2: move.l (a7)+,d0 move.w d2,-(a7) ;result trap #1 move.w (a7)+,d0 ;in d0 movem.l (a7)+,d2/d6/a0 rts ;---------------------------------------------------- ;Input> d0.w : Handle of the program ; a0.l : buffer (18 bytes at min ) ;Out> d0.w : 0 if error ; else sucess ;---------------------------------------------------- _GetTIOSFileName: apilib@001A movem.l d1-d4/a0-a4,-(a7) move.l a0,a2 move.w d0,d1 ;Handle of prog bsr _vat_FindHdlEverywhere tst.w d2 beq.s \error move.l a1,a4 ;File Name addr move.w d0,d1 ;Hdl of folder API92_FOLDHDL d0 bsr _vat_FindHdl tst.w d2 beq.s \error bsr cpy8str move.b #'\',(a2)+ move.l a4,a1 bsr cpy8str clr.b (a2)+ moveq.w #1,d0 ;success \exit movem.l (a7)+,d1-d4/a0-a4 rts \error: clr.w d0 ;error bra.s \exit ; a1->a2 cpy8str: moveq.w #7,d0 ;8 laps \cpy tst.b (a1) beq.s \out move.b (a1)+,(a2)+ dbra.s d0,\cpy \out rts ;------------------------------------------------------ ; GetKeyInitDelayAddr ;Input> nothing ;Out> d0.w : Value of KeyInitDelay variable ;------------------------------------------------------ _util_GetKeyInitDelay: apilib@0074: ;#ifdef TIPLUS move.w #100,-(a7) TIOS_CALL OSInitKeyInitDelay move.w d0,(a7) ;previous value on stack TIOS_CALL OSInitKeyInitDelay ;restore previous value move.w (a7)+,d0 ;#endif rts ;------------------------------------------------------ ; GetBetweenKeyDelayAddr ;Input> nothing ;Out> d0.w : Value of KeyInitDelay variable ;------------------------------------------------------ _util_GetBetweenKeyDelay: apilib@0075: ;#ifdef TIPLUS move.w #100,-(a7) TIOS_CALL OSInitBetweenKeyDelay move.w d0,(a7) TIOS_CALL OSInitBetweenKeyDelay move.w (a7)+,d0 ;#endif rts